home *** CD-ROM | disk | FTP | other *** search
- /*
- File: dlpiether.c
-
- Contains: Code to implement the DLPI template
- ** dlpiether.c 5.46, last change 28 Mar 1996
-
- Copyright: © 1995, 1996 by Mentat Inc. and Apple Computer, Inc., all rights reserved.
-
- */
-
- #ifndef __DLPIETHER__
- #include "dlpiether.h"
- #endif
-
- /*******************************************************************************
- ** Some defines
- ********************************************************************************/
-
- #define nilp(type) ((type*)0)
- #define A_CNT(arr) (sizeof(arr)/sizeof(arr[0]))
- #define A_END(arr) (&arr[A_CNT(arr)])
- #define A_LAST(arr) (&arr[A_CNT(arr)-1])
- #define mi_qprocson(a) /* Nothing */
- #define put(q, mp) puthere(q, mp);
-
- int mi_strlog (queue_t * q, ...);
-
- /*******************************************************************************
- ** Some private structures
- ********************************************************************************/
-
- /*
- * Structure used for maintaining the list of multicast and physical
- * addresses in dle_hw_addr_list. The dleax_dlea portion of this structure
- * is used by board-specific code to find out which multicast addresses
- * should be received; this list is passed to the hardware address
- * filter routine.
- */
- typedef struct dle_addrx_s {
- dle_addr_t dleax_dlea; /* Address structure. */
- UInt32 dleax_ref_cnt; /* Number of streams using this addr. */
- } dle_addrx_t;
-
- #define dleax_next dleax_dlea.dlea_next
- #define dleax_addr dleax_dlea.dlea_addr
-
- /*
- * Structure for holding bind information; one is created for each DL_BIND_REQ,
- * for each DL_SUBS_BIND_REQ/DL_PEER_BIND, and for each promiscuous setting.
- * These structures are linked into the requesting stream's dcl structure
- * (through bind_next) and are linked into the dle's bind hash table
- * or one of the promiscuous match lists (through bind_hash_next and/or
- * bind_sap_next).
- *
- * Each hash chain in the hash table is linked with the bind_hash_next field.
- * The bound sap is used to hash to the right chain in the table, and there is
- * exactly one bind structure in the chain followed with bind_hash_next for any
- * sap. Any additional binds for the same sap are linked through bind_sap_next
- * from the bind in the primary hash chain.
- *
- * The promiscuous lists are headed by various fields in the dle structure
- * (dle_match_any, etc.). These lists are linked with the bind_sap_next field.
- * This allows dle_inbound_finish to loop through all similar bind structures
- * following bind_sap_next.
- */
- typedef struct bind_s {
- struct bind_s * bind_next; /* Link field for dle_bind_list */
- UInt32 bind_flags; /* Defined below */
- UInt32 bind_sap; /* Sap value: Ethertype or SNAP value */
- struct bind_s * bind_hash_next;/* Link field for hash table */
- struct dcl_s * bind_dcl; /* Back pointer to client stream */
- struct bind_s * bind_sap_next;/* Link field for multiple binds to */
- /* same sap or for multiple promiscuous*/
- /* settings of the same flavor. */
- } bind_t;
-
- /*
- * Bind flags.
- * NOTE: the SAP_HASH_BITS must fit within bits given by DLE_SAP_HASH_VALUE.
- */
- #define F_BIND_SNAP 0x1 /* 802.2 with SNAP binding */
- #define F_BIND_802 0x2 /* Vanilla, non-SNAP binding */
- #define F_BIND_IPX 0x4 /* Raw formatting for old-style IPX */
- #define F_BIND_SNAP_PENDING 0x8 /* Temporary flag used between bind */
- /* and subsbind */
- #define F_BIND_SAP_HASH_BITS (F_BIND_SNAP | F_BIND_802 | F_BIND_IPX | F_BIND_SNAP_PENDING)
-
- #define F_BIND_MATCH_MATCHED 0x20
- #define F_BIND_HASH 0x40
-
- /* Inbound packet may be any 802.x format packet */
- #define F_BIND_MATCH_ANY_802 0x100
-
- /* Inbound address may be anything */
- #define F_BIND_MATCH_ANY 0x200
-
- /* Inbound addr may be any multicast */
- #define F_BIND_MATCH_ANY_MULTICAST 0x400
-
- /*
- * Inbound address must match dle_current_addr. Note that this flag is
- * not checked when processing inbound packets for binds marked F_BIND_HASH;
- * it is always on in this case. The flag is set when the creating the
- * binds for debugging purposes; one of the match flags is always set.
- */
- #define F_BIND_MATCH_LOCAL 0x800
-
- #define F_BIND_PROMISCUOUS_MASK (F_BIND_MATCH_ANY | F_BIND_MATCH_ANY_MULTICAST\
- | F_BIND_MATCH_ANY_802 | F_BIND_MATCH_MATCHED)
-
- #define F_BIND_AUTO_XID 0x1000
- #define F_BIND_AUTO_TEST 0x2000
-
- /*
- * Flag values for dcl_flags. These are maintained by the common code
- * and should *not* be changed by the board-specific code.
- */
- #define F_DCL_PROMISC_PHYS 0x1
- #define F_DCL_PROMISC_MULTI 0x2
- #define F_DCL_PROMISC_SAP 0x4
- #define F_DCL_802 0x8
- #define F_DCL_SNAP 0x10
- #define F_DCL_IPX 0x20
- #define F_DCL_NOT_FAST_PATH 0x80 /* in dlpiether.h also */
- #define F_DCL_AUTO_XID 0x100
- #define F_DCL_AUTO_TEST 0x200
- #define F_DCL_WANTS_RS_HEADER 0x400
- #define F_DCL_WANTS_ERROR_PACKETS 0x800
- #define F_DCL_PROMISCUOUS_MASK (F_DCL_PROMISC_PHYS | F_DCL_PROMISC_MULTI \
- | F_DCL_PROMISC_SAP)
-
- #define DLE_SAP_HASH_VALUE(sap,flags) (((sap) ^ ((flags) & F_BIND_SAP_HASH_BITS)) & 63)
-
- /* Offsets to LLC header fields */
- #define LLC_DSAP_OFFSET 14
- #define LLC_SSAP_OFFSET 15
- #define LLC_CONTROL_OFFSET 16
-
- /* Definitions for handling 802.2 LLC Test and XID messages. */
- #define LLC_DATA_VALUE 0x3
- #define LLC_TEST_VALUE 0xE3
- #define LLC_XID_VALUE 0xAF
- #define LLC_RESPONSE_BIT 0x01
- #define LLC_POLL_FINAL_FLAG 0x10 /* Poll/Final bit */
- #define XID_FORMAT_IDENTIFIER 0x81
-
- /* Internal dispatch table for DLPI primitives. */
- typedef struct dle_dlpi_dispatch_s {
- UInt32 dledd_primitive;
- int (*dledd_func)(queue_t *, mblk_t *);
- UInt16 dledd_min_len; /* Min request length required */
- UInt16 dledd_reallocb_len; /* Maximum ack length required */
- UInt16 dledd_flags; /* Defined below */
- } dle_dlpi_dispatch_t;
-
- /* Flag values for dledd_flags */
- #define F_MIEDD_OK_ACK_NEEDED 0x1
-
- static bind_t * dle_bind_alloc(dcl_t * dcl, UInt32 sap, UInt32 flags);
- static void dle_bind_free(dle_t * dle, bind_t * bind);
- static void dle_bind_disable(dle_t * dle, bind_t * bind);
- static void dle_bind_enable(bind_t * bind);
- static bind_t * dle_bind_from_stuff(dcl_t * dcl, UInt32 flags, UInt32 sap);
- static void dle_bind_hash_insert(bind_t * bind);
- static void dle_bind_hash_remove(dle_t * dle, bind_t * bind);
- static int dle_bind_req(queue_t * q, mblk_t * mp);
- static bind_t ** dle_bindp_from_flags_and_sap(dle_t * dle, UInt32 flags, UInt32 sap);
- static int dle_disabmulti_req(queue_t * q, mblk_t * mp);
- static int dle_enabmulti_req(queue_t * q, mblk_t * mp);
- static int dle_get_statistics_req(queue_t * q, mblk_t * mp);
- static UInt32 dle_hw_addr_dec_ref(dle_t * dle, UInt8 * addr);
- static UInt32 dle_hw_addr_inc_ref (dle_t * dle, UInt8 * addr);
- static void dle_hw_address_filter_reset(dle_t * dle);
- static void dle_inbound_802_broadcast(dle_t * dle, mblk_t * mp);
- static mblk_t * dle_inbound_special(dcl_t * dcl, mblk_t * mp1, size_t msg_len,
- UInt8 * rptr, UInt32 flags);
- static void dle_inbound_xidtest(dle_t * dle, mblk_t * mp);
- static int dle_info_req(queue_t * q, mblk_t * mp);
- static dle_addr_t ** dle_dleap_from_addr(dcl_t * dcl, UInt8 * addr);
- static int dle_phys_addr_req(queue_t * q, mblk_t * mp);
- static int dle_promiscoff_req(queue_t * q, mblk_t * mp);
- static int dle_promiscon_req(queue_t * q, mblk_t * mp);
- static void dle_send_xidtest_response(bind_t * bind, mblk_t * mp);
- static int dle_subs_bind_req(queue_t * q, mblk_t * mp);
- static int dle_subs_unbind_req(queue_t * q, mblk_t * mp);
- static void dle_remove_all_my_binds(dcl_t * dcl);
- static int dle_unbind_req(queue_t * q, mblk_t * mp);
- static mblk_t * dle_wput_error(queue_t * q, mblk_t * mp, int dl_err, int sys_err);
- static mblk_t * dle_wput_ioctl(queue_t * q, mblk_t * mp);
- static mblk_t * dle_wput_proto(queue_t * q, mblk_t * mp);
- static int dle_xidtest(queue_t * q, mblk_t * mp);
-
- static dle_dlpi_dispatch_t dle_dlpi_dispatch_table[] = {
- { DL_INFO_REQ, dle_info_req, sizeof(dl_info_req_t),
- sizeof(dl_info_ack_t) + 6 + 13 },
- { DL_BIND_REQ, dle_bind_req, sizeof(dl_bind_req_t),
- sizeof(dl_bind_ack_t) + 8 },
- { DL_ENABMULTI_REQ, dle_enabmulti_req, sizeof(dl_enabmulti_req_t) + 6,
- sizeof(dl_ok_ack_t), F_MIEDD_OK_ACK_NEEDED },
- { DL_DISABMULTI_REQ, dle_disabmulti_req, sizeof(dl_disabmulti_req_t) + 6,
- sizeof(dl_ok_ack_t), F_MIEDD_OK_ACK_NEEDED },
- { DL_UNBIND_REQ, dle_unbind_req, sizeof(dl_unbind_req_t),
- sizeof(dl_ok_ack_t), F_MIEDD_OK_ACK_NEEDED },
- { DL_SUBS_BIND_REQ, dle_subs_bind_req, sizeof(dl_subs_bind_req_t),
- sizeof(dl_subs_bind_ack_t) + 13 },
- { DL_SUBS_UNBIND_REQ, dle_subs_unbind_req, sizeof(dl_unbind_req_t),
- sizeof(dl_ok_ack_t), F_MIEDD_OK_ACK_NEEDED },
- { DL_PROMISCON_REQ, dle_promiscon_req, sizeof(dl_promiscon_req_t),
- sizeof(dl_ok_ack_t), F_MIEDD_OK_ACK_NEEDED },
- { DL_PROMISCOFF_REQ, dle_promiscoff_req, sizeof(dl_promiscoff_req_t),
- sizeof(dl_ok_ack_t), F_MIEDD_OK_ACK_NEEDED },
- { DL_PHYS_ADDR_REQ, dle_phys_addr_req, sizeof(dl_phys_addr_req_t),
- sizeof(dl_phys_addr_ack_t) + 6 },
- { DL_GET_STATISTICS_REQ, dle_get_statistics_req,sizeof(dl_get_statistics_req_t),
- sizeof(dl_get_statistics_ack_t)
- + sizeof(dle_interface_status_t)
- + sizeof(dle_ethernet_status_t)
- + 2 * sizeof(TOptionHeader) },
- { DL_TEST_REQ, dle_xidtest, sizeof(dl_test_req_t) + 6,
- sizeof(dl_test_req_t), 0 },
- { DL_TEST_RES, dle_xidtest, sizeof(dl_test_res_t) + 6,
- sizeof(dl_test_res_t), 0 },
- { DL_XID_REQ, dle_xidtest, sizeof(dl_xid_req_t) + 6,
- sizeof(dl_xid_req_t), 0 },
- { DL_XID_RES, dle_xidtest, sizeof(dl_xid_res_t) + 6,
- sizeof(dl_xid_res_t), 0 }
- };
-
- /*
- * Allocate and initialize a bind structure. Called by dle_bind_req,
- * dle_subs_bind_req and dle_promiscon_req.
- */
- static bind_t *
- dle_bind_alloc (dcl_t * dcl, UInt32 sap, UInt32 flags)
- {
- bind_t * bind;
-
- bind = (bind_t *)OTAllocMem(sizeof(*bind));
- if (bind == nilp(bind_t))
- return bind;
- bind->bind_sap = sap;
- bind->bind_dcl = dcl;
- bind->bind_flags = flags;
- return bind;
- }
-
- static void
- dle_bind_free (dle_t * dle, bind_t * bind)
- {
- while (dle->dle_intr_active)
- ;
- OTFreeMem(bind);
- }
-
- /*
- * Remove a bind structure from the dle's list of all binds and from the
- * hash table or match list. This is called by dle_remove_all_my_binds,
- * dle_subs_unbind_req, and dle_promiscoff_req.
- */
- static void
- dle_bind_disable (dle_t * dle, bind_t * bind)
- {
- unsigned int changed = 0;
-
- dle_bind_hash_remove(dle, bind);
-
- if ((bind->bind_flags & F_BIND_MATCH_ANY_MULTICAST)
- && --dle->dle_match_any_multicast_count == 0
- && dle->dle_match_any_count == 0)
- changed++;
-
- if ((bind->bind_flags & F_BIND_MATCH_ANY)
- && --dle->dle_match_any_count == 0)
- changed++;
-
- if (changed != 0)
- dle_hw_address_filter_reset(dle);
-
- /* If there are no bindings, then shut the hardware down. */
- if (--dle->dle_bound_count == 0)
- (*dle->dle_hw.dlehw_stop)(dle_to_hw(dle));
- }
-
- /*
- * Enable a new bind for the dle. If the board has not been kicked alive yet,
- * do so by calling the hardware start routine. This routine is called by
- * dle_bind_req and dle_subs_bind_req.
- *
- * Safety note: all binds for the dle must be inserted into the hash table by
- * calling this routine and not by calling dle_bind_hash_insert directly.
- * dle_remove_all_my_binds calls dle_bind_disable for all binds in the dcl's
- * bind list and dle_bind_disable decrements dle_bound_count for every bind
- * removed.
- */
- static void
- dle_bind_enable (bind_t * bind)
- {
- dle_t * dle;
- unsigned int changed;
-
- dle_bind_hash_insert(bind);
-
- /* Determine if we need to change the address filters on the hardware. */
- dle = dcl_to_dle(bind->bind_dcl);
- changed = (dle->dle_bound_count++ == 0);
-
- if (bind->bind_flags & F_BIND_MATCH_ANY)
- changed |= (dle->dle_match_any_count++ == 0);
-
- if (bind->bind_flags & F_BIND_MATCH_ANY_MULTICAST)
- changed |= (dle->dle_match_any_multicast_count++ == 0);
-
- if (changed != 0)
- dle_hw_address_filter_reset(dle);
-
- /*
- * If this is the first bind for this board, call the start routine to
- * to initialize receive and transmit operations. This is called after
- * the possible call dle_hw_address_filter_reset in case we've changed
- * the physical address or multicast filters.
- */
- if (dle->dle_bound_count == 1)
- (*dle->dle_hw.dlehw_start)(dle_to_hw(dle));
- }
-
- /*
- * Find the start of the hash chain or match list for the requested flags
- * and sap. This routine is called when looking up binds and when inserting
- * new binds into the lookup tables.
- */
- static bind_t **
- dle_bindp_from_flags_and_sap (dle_t * dle, UInt32 flags, UInt32 sap)
- {
- bind_t ** bindp;
- bind_t * bind;
-
- if (flags & F_BIND_HASH) {
- bindp = (bind_t **)&dle->dle_sap_hash_tbl[DLE_SAP_HASH_VALUE(sap, flags)];
- for ( ; (bind = bindp[0]) != nilp(bind_t); bindp = &bind->bind_hash_next) {
- if (bind->bind_sap == sap)
- break;
- }
- } else if (flags & F_BIND_MATCH_MATCHED)
- bindp = (bind_t **)&dle->dle_match_matched;
- else if (flags & F_BIND_MATCH_ANY_MULTICAST)
- bindp = (bind_t **)&dle->dle_match_any_multicast;
- else if (flags & F_BIND_MATCH_ANY_802)
- bindp = (bind_t **)&dle->dle_match_any_802;
- else
- bindp = (bind_t **)&dle->dle_match_any;
- return bindp;
- }
-
- /*
- * Find the bind structure in the hash table for this stream (dcl) and for
- * the requested flags and sap.
- */
- static bind_t *
- dle_bind_from_stuff (dcl_t * dcl, UInt32 flags, UInt32 sap)
- {
- dle_t * dle = dcl_to_dle(dcl);
- bind_t * bind = *dle_bindp_from_flags_and_sap(dle, flags, sap);
-
- for ( ; bind != nilp(bind_t); bind = bind->bind_sap_next ) {
- if ((bind->bind_flags & flags) == flags
- && bind->bind_dcl == dcl)
- return bind;
- }
- return bind;
- }
-
- /*
- * Add a new bind structure to the dle's list of all binds (dle_bind_list) and
- * to the sap hash table or appropriate promiscuous match list. This is called
- * by dle_bind_enable and by dle_subs_bind_req.
- */
- static void
- dle_bind_hash_insert (bind_t * bind)
- {
- bind_t ** bindp;
- bind_t * bind1;
- dle_t * dle = dcl_to_dle(bind->bind_dcl);
-
- bind->bind_sap_next = nilp(bind_t);
- bind->bind_hash_next = nilp(bind_t);
-
- /* Add the bind to the list of all binds. */
- bind->bind_next = dle->dle_bind_list;
- dle->dle_bind_list = bind;
-
- /* Add the bind to the sap hash table. */
- bindp = dle_bindp_from_flags_and_sap(dle, bind->bind_flags
- , bind->bind_sap);
- bind1 = bindp[0];
- if (bind1 != nilp(bind_t)) {
- /*
- * If there are other binds for the same sap, then we push the
- * others down in the list (by linking them through our
- * bind_sap_next) and make this bind structure the topmost
- * in the hash chain.
- */
- bind->bind_hash_next = bind1->bind_hash_next;
- bind1->bind_hash_next = nilp(bind_t);
- bind->bind_sap_next = bind1;
- }
- bindp[0] = bind;
- }
-
- /*
- * Remove a bind structure from the dle's dle_bind_list and from the dle
- * hash table. Primarily called by dle_bind_disable.
- */
- static void
- dle_bind_hash_remove (dle_t * dle, bind_t * bind)
- {
- bind_t * bind1;
- bind_t ** bindp, * sap_top, * sap_next;
-
- /* Find the structure in the dle's list of all binds. */
- for (bindp = (bind_t **)&dle->dle_bind_list; ; bindp = &bind1->bind_next) {
- bind1 = bindp[0];
- if (bind1 == nilp(bind_t))
- return;
- if (bind1 == bind)
- break;
- }
- /* Remove the bind from the dle_bind_list. */
- bindp[0] = bind->bind_next;
- bind->bind_next = nilp(bind_t);
-
- /* Now locate the structure's location in the sap hash table. */
- bindp = dle_bindp_from_flags_and_sap(dle, bind->bind_flags, bind->bind_sap);
- sap_top = bindp[0];
- sap_next = bind->bind_sap_next;
- if (sap_top == bind) {
- bind_t * hash_next = bind->bind_hash_next;
- /*
- * This is the first bind in the hash table for this sap.
- * If there are other binds for this sap, then promote the
- * next one to be the top of the sap chain.
- */
- if (sap_next != nilp(bind_t)) {
- sap_next->bind_hash_next = hash_next;
- hash_next = sap_next;
- }
- /* Get this bind out of the hash chain. */
- bindp[0] = hash_next;
- } else {
- /*
- * There are multiple binds for this sap in the hash chain and
- * we are not the topmost one. Find our location in the sap
- * chain and remove us from the list.
- */
- while (sap_top->bind_sap_next != bind) {
- sap_top = sap_top->bind_sap_next;
- if (sap_top == nilp(bind_t))
- return;
- }
- sap_top->bind_sap_next = sap_next;
- }
- }
-
- /* Process DL_BIND_REQ messages from dle_wput. */
- static int
- dle_bind_req (queue_t * q, mblk_t * mp)
- {
- UInt8 * addr;
- dl_bind_ack_t * dlba;
- dl_bind_req_t * dlbr = (dl_bind_req_t *)mp->b_rptr;
- dcl_t * dcl = (dcl_t *)q->q_ptr;
- UInt32 sap, flags, new_dcl_flags;
- bind_t * bind;
-
- if (dcl->dcl_state != DL_UNBOUND || (dcl->dcl_flags & F_DCL_PROMISCUOUS_MASK))
- return DL_OUTSTATE;
-
- /* We only support connectionless mode DLPI. */
- if (dlbr->dl_service_mode != DL_CLDLS || dlbr->dl_max_conind != 0)
- return DL_UNSUPPORTED;
-
- /*
- * Determine from the sap what type of bind this is: 802.2,
- * Ethertype or IPX.
- */
- sap = dlbr->dl_sap;
- flags = F_BIND_HASH | F_BIND_MATCH_LOCAL;
- new_dcl_flags = 0;
- /* Check for an 802.2 bind first. */
- if (sap <= 0xFE) {
- /*
- * Don't allow binds to 802.2 Group saps.
- * Group saps may only be bound by a DL_SUBSBIND_REQ with
- * DL_PEER_BIND specified.
- */
- if (sap & 0x1)
- return DL_BADADDR;
-
- if (sap == 0xAA) {
- /*
- * A sap of 0xAA means this will be a SNAP stream and
- * there will be a DL_SUBSBIND_REQ message later. The
- * bind flags here will put the structure into the hash
- * table such that inbound packets will not match.
- * During DL_SUBSBIND_REQ processing, the bind structure
- * will be located and modified with the SNAP info;
- * then it will be placed back in the hash table such
- * that inbound packets will be properly dispatched.
- */
- flags |= F_BIND_SNAP_PENDING;
- } else
- flags |= F_BIND_802;
-
- if (dlbr->dl_xidtest_flg & DL_AUTO_XID) {
- flags |= F_BIND_AUTO_XID;
- new_dcl_flags |= F_DCL_AUTO_XID;
- }
- if (dlbr->dl_xidtest_flg & DL_AUTO_TEST) {
- flags |= F_BIND_AUTO_TEST;
- new_dcl_flags |= F_DCL_AUTO_TEST;
- }
- new_dcl_flags |= F_DCL_802;
- } else if (sap == 0xFF) {
- flags |= F_BIND_IPX;
- new_dcl_flags |= F_DCL_IPX;
- } else if (sap > kMaxDIXSAP || sap < kMinDIXSAP)
- return DL_BADADDR;
-
- /* Create a new bind structure for this sap. */
- bind = dle_bind_alloc(dcl, sap, flags);
- if (bind == nilp(bind_t))
- return DL_SYSERR;
-
- /*
- * Put the new bind structure in the hash table. dle_bind_enable will
- * also link the structure into the dle, and call the board's start
- * routine if this is the first bind.
- */
- dle_bind_enable(bind);
-
- /*
- * Save state in the dcl. The sap and flags are saved so that outbound
- * packets can be properly formatted if the complete sap information is
- * not included in DL_UNITDATA_REQ messages (this happens if the address
- * length is 6, including just the destination Ethernet address).
- */
- dcl->dcl_state = DL_IDLE;
- dcl->dcl_sap = sap;
- dcl->dcl_flags |= new_dcl_flags;
-
- /* Create the DL_BIND_ACK message and pass upstream to the requestor. */
- dlba = (dl_bind_ack_t *)mp->b_rptr;;
- dlba->dl_primitive = DL_BIND_ACK;
- dlba->dl_sap = sap;
- dlba->dl_addr_length = 8;
- dlba->dl_addr_offset = sizeof(dl_bind_ack_t);
- dlba->dl_max_conind = 0;
-
- dlba->dl_xidtest_flg = 0;
- if (flags & F_BIND_AUTO_XID)
- dlba->dl_xidtest_flg |= DL_AUTO_XID;
- if (flags & F_BIND_AUTO_TEST)
- dlba->dl_xidtest_flg |= DL_AUTO_TEST;
-
- addr = (UInt8 *)&dlba[1];
- addr[6] = (sap >> 8);
- addr[7] = sap & 0xFF;
- mp->b_wptr = &addr[8];
- mp->b_datap->db_type = M_PCPROTO;
- bcopy(dcl_to_dle(dcl)->dle_current_addr, addr, 6);
- qreply(q, mp);
- return 0;
- }
-
- /* STREAMS close entry point called by board-specific close code. */
- int
- dle_close (queue_t * q)
- {
- dcl_t * dcl = (dcl_t *)q->q_ptr;
- int err;
- dle_t * dle;
- dle_addr_t * dlea;
- int changes = 0;
-
- if (dcl == nilp(dcl_t))
- return 0;
-
- /*
- * Remove all entries in the bind hash table associated with
- * this stream.
- */
- dle_remove_all_my_binds(dcl);
-
- /*
- * Decrement the reference count on all multicast addresses (or
- * additional physical addresses) associated with this stream. If
- * there are no other streams using an address, then dle_hw_addr_dec_ref
- * will delete the associated structure kept in the dle_hw_addr_list.
- */
- dle = dcl_to_dle(dcl);
- while ((dlea = dcl->dcl_addr_list) != nilp(dle_addr_t)) {
- dcl->dcl_addr_list = dlea->dlea_next;
- if (dle_hw_addr_dec_ref(dle, dlea->dlea_addr) == 0)
- changes = 1;
- /* Delete the dcl's copy of the address structure. */
- OTFreeMem(dlea);
- }
- /*
- * Change the chip's address filtering after all possible
- * changes have been made.
- */
- if (changes != 0)
- dle_hw_address_filter_reset(dle);
-
- /* Delete the instance structure for the stream. */
- err = mi_close_comm(&dle->dle_instance_head, q);
-
- /*
- * Decrement the reference count on the shared instance structure for
- * this board/port. This count is maintained, but not currently used
- * for anything significant by the dlpiether shared code.
- */
- dle->dle_refcnt--;
- return err;
- }
-
- /* Process DL_DISABMULTI_REQ messages for dle_wput. */
- static int
- dle_disabmulti_req (queue_t * q, mblk_t * mp)
- {
- UInt8 * addr;
- dcl_t * dcl = (dcl_t *)q->q_ptr;
- dl_disabmulti_req_t * dldmr = (dl_disabmulti_req_t *)mp->b_rptr;
- dle_addr_t * dlea, ** dleap;
- dle_t * dle;
-
- if (dcl->dcl_state == DL_UNATTACHED)
- return DL_OUTSTATE;
-
- /* Sanity checks on the request. */
- addr = mi_offset_param(mp, dldmr->dl_addr_offset, dldmr->dl_addr_length);
- if (addr == nilp(UInt8) || dldmr->dl_addr_length != 6)
- return DL_BADADDR;
-
- /* Find the dlea structure for the address to be disabled. */
- addr = &mp->b_rptr[dldmr->dl_addr_offset];
- dleap = dle_dleap_from_addr(dcl, addr);
- dlea = dleap[0];
- if (dlea == nilp(dle_addr_t))
- return DL_NOTENAB;
-
- dleap[0] = dlea->dlea_next;
- dle = dcl_to_dle(dcl);
- if (dle_hw_addr_dec_ref(dle, dlea->dlea_addr) == 0) {
- /*
- * If this was the last reference to the address, then allow
- * the hardware to change its address filtering.
- */
- dle_hw_address_filter_reset(dle);
- }
-
- OTFreeMem(dlea);
- return 0;
- }
-
- /* Process DL_ENABMULTI_REQ messages for dle_wput. */
- static int
- dle_enabmulti_req (queue_t * q, mblk_t * mp)
- {
- UInt8 * addr;
- dcl_t * dcl = (dcl_t *)q->q_ptr;
- dl_enabmulti_req_t * dlemr = (dl_enabmulti_req_t *)mp->b_rptr;
- dle_t * dle;
- dle_addr_t * dlea, ** dleap;
- UInt32 ref_cnt;
-
- if (dcl->dcl_state == DL_UNATTACHED)
- return DL_OUTSTATE;
-
- addr = &mp->b_rptr[dlemr->dl_addr_offset];
-
- /* Sanity checks on the request message. */
- addr = mi_offset_param(mp, dlemr->dl_addr_offset, dlemr->dl_addr_length);
- if (addr == nilp(UInt8) || dlemr->dl_addr_length != 6 || (addr[0] & 0x1) == 0)
- return DL_BADADDR;
-
- /*
- * Look for this address in the dcl's list of multicast addresses
- * or just get the pointer where we can add a new dlea structure.
- */
- dleap = dle_dleap_from_addr(dcl, addr);
- if (dleap[0] != nilp(dle_addr_t)) {
- /*
- * If this address is already enabled for this stream,
- * then there's nothing more to do here.
- */
- return 0;
- }
-
- /*
- * Create a new dlea structure for this multicast address. The dlea
- * structure is linked onto the dcl's list of enabled addresses.
- * This list allows dle_close to delete all the addresses associated
- * with the dcl when the stream closes.
- */
- dlea = OTAllocMem(sizeof(dle_addr_t));
- if (dlea == nilp(dle_addr_t))
- return DL_TOOMANY;
-
- /*
- * Associate this address with the dle. dle_hw_addr_inc_ref will
- * either create another new dlea to keep on the dle's address list
- * or it will just increment the reference count of an existing
- * dlea for this address. This list is passed to the board-specific
- * code for address filtering; it contains all addresses enabled
- * by all streams associated with the device.
- */
- dle = dcl_to_dle(dcl);
- ref_cnt = dle_hw_addr_inc_ref(dle, addr);
- if (ref_cnt == 0) {
- OTFreeMem(dlea);
- return DL_TOOMANY;
- }
-
- /* Copy the requested multicast address into the dlea. */
- bcopy(addr, dlea->dlea_addr, 6);
-
- /* And link the structure into the dcl list. */
- dlea->dlea_next = nilp(dle_addr_t);
- dleap[0] = dlea;
-
- if (ref_cnt == 1) {
- /*
- * If this is a new address, then allow the hardware
- * to adjust its address filtering.
- */
- dle_hw_address_filter_reset(dle);
- }
- return 0;
- }
-
- /* Handle DL_GET_STATISTICS_REQ messages from dle_wput. */
- static int
- dle_get_statistics_req (queue_t * q, mblk_t * mp)
- {
- UInt8 * wptr;
- dcl_t * dcl;
- dle_t * dle;
- dl_get_statistics_ack_t * dlsa;
- TOptionHeader * topt;
-
- dcl = (dcl_t *)q->q_ptr;
- dle = dcl_to_dle(dcl);
-
- mp->b_datap->db_type = M_PCPROTO;
- dlsa = (dl_get_statistics_ack_t *)mp->b_rptr;
- dlsa->dl_primitive = DL_GET_STATISTICS_ACK;
- dlsa->dl_stat_length = sizeof(dle_interface_status_t)
- + sizeof(dle_ethernet_status_t)
- + 2 * sizeof(TOptionHeader);
- dlsa->dl_stat_offset = sizeof(dl_get_statistics_ack_t);
-
- wptr = &mp->b_rptr[sizeof(dl_get_statistics_ack_t)];
- topt = (TOptionHeader *)wptr;
- topt->len = sizeof(TOptionHeader) + sizeof(dle->dle_istatus);
- topt->level = DLPI_XTI_LEVEL;
- topt->name = DL_INTERFACE_MIB;
- topt->status = T_SUCCESS;
- wptr += sizeof(TOptionHeader);
- bcopy((char *)&dle->dle_istatus, wptr, sizeof(dle->dle_istatus));
- wptr += sizeof(dle->dle_istatus);
-
- topt = (TOptionHeader *)wptr;
- topt->len = sizeof(TOptionHeader) + sizeof(dle->dle_estatus);
- topt->level = DLPI_XTI_LEVEL;
- topt->name = DL_ETHERNET_MIB;
- topt->status = T_SUCCESS;
- wptr += sizeof(TOptionHeader);
- bcopy((char *)&dle->dle_estatus, wptr, sizeof(dle->dle_estatus));
- wptr += sizeof(dle->dle_estatus);
- mp->b_wptr = wptr;
- qreply(q, mp);
- return 0;
- }
-
- /*
- * Find the specified address in the list of addresses for this dle and return
- * a pointer to the cell pointing to the appropriate structure. If the
- * address is not in the list, then the returned pointer points to the location
- * in which a new structure may be added to the list. This routine is used
- * by dle_hw_addr_inc_ref and dle_hw_addr_dec_ref to add and delete multicast
- * addresses.
- */
- static dle_addrx_t **
- dle_hw_dleaxp_from_addr (dle_t * dle, UInt8 * addr)
- {
- dle_addrx_t ** dleaxp = (dle_addrx_t **)&dle->dle_hw_addr_list;
- dle_addrx_t * dleax;
- size_t u1;
-
- for ( ; (dleax = dleaxp[0]) != nilp(dle_addrx_t); dleaxp = (dle_addrx_t **)&dleax->dleax_next ) {
- UInt8 sum = 0;
-
- for (u1 = 0; u1 < 6; u1++)
- sum |= dleax->dleax_addr[u1] ^ addr[u1];
- if (sum == 0)
- break;
- }
- return dleaxp;
- }
-
- /*
- * Associate a multicast or physical address with a dle. If the address
- * is already being used by another stream, then the address' reference
- * count is incremented. If this is a new address, then a new structure
- * is allocated and added to the dle_hw_addr_list. This routine is called
- * by dle_enabmulti_req.
- */
- static UInt32
- dle_hw_addr_inc_ref (dle_t * dle, UInt8 * addr)
- {
- dle_addrx_t ** dleaxp;
- dle_addrx_t * dleax;
-
- dleaxp = dle_hw_dleaxp_from_addr(dle, addr);
- dleax = dleaxp[0];
- if (dleax == nilp(dle_addrx_t)) {
- dleax = OTAllocMem(sizeof(dle_addrx_t));
- if (dleax == nilp(dle_addrx_t))
- return 0;
- bzero(dleax, sizeof(dle_addrx_t));
- bcopy(addr, dleax->dleax_addr, 6);
- dleax->dleax_next = nilp(dle_addr_t);
- dleaxp[0] = dleax;
- }
- return ++dleax->dleax_ref_cnt;
- }
-
- /*
- * Disassociate a multicast or physical address with a dle. If this is the
- * last reference to the address, then the corresponding structure is removed
- * from dle_hw_addr_list and freed. This routine is called by dle_disabmulti_req.
- */
- static UInt32
- dle_hw_addr_dec_ref (dle_t * dle, UInt8 * addr)
- {
- dle_addrx_t ** dleaxp = dle_hw_dleaxp_from_addr(dle, addr);
- dle_addrx_t * dleax = dleaxp[0];
-
- if (dleax == nilp(dle_addrx_t))
- return 0;
- if (dleax->dleax_ref_cnt-- <= 1) {
- dleaxp[0] = (dle_addrx_t *)dleax->dleax_next;
- OTFreeMem(dleax);
- return 0;
- }
- return dleax->dleax_ref_cnt;
- }
-
- /*
- * Change the address filtering for the board by calling the hardware address
- * filter reset routine. This routine is called by dle_enabmulti_req,
- * dle_disabmulti_req, dle_bind_enable, and dle_bind_disable.
- */
- static void
- dle_hw_address_filter_reset (dle_t * dle)
- {
- bind_t * bind;
- size_t count = 0;
- dle_addrx_t * dleax;
- size_t error_count = 0;
-
- dleax = dle->dle_hw_addr_list;
- for ( ; dleax; dleax = (dle_addrx_t *)dleax->dleax_next)
- count++;
-
- for (bind = dle->dle_match_any; bind != nilp(bind_t); bind = bind->bind_next) {
- if (bind->bind_dcl->dcl_flags & F_DCL_WANTS_ERROR_PACKETS) {
- error_count = 1;
- break;
- }
- }
-
- (*dle->dle_hw.dlehw_address_filter_reset)(dle_to_hw(dle)
- , dle->dle_hw_addr_list, count, dle->dle_match_any_count
- , dle->dle_match_any_multicast_count, 1, error_count);
- }
-
- /* Handle all inbound packets destined for the 802.2 Global sap. */
- static void
- dle_inbound_802_broadcast (dle_t * dle, mblk_t * mp)
- {
- bind_t * bind;
- dl_unitdata_ind_t * dlui;
- UInt8 * dst, * hdr;
- mblk_t * mp1;
- UInt8 sum;
-
- hdr = mp->b_rptr;
- mp->b_rptr += 17; /* Strip the Ethernet and LLC headers. */
-
- /*
- * We only want to handle these packets if they
- * were sent to our local address or to a
- * broadcast address. No multicasts.
- */
- {
- UInt8 * addr = dle->dle_current_addr;
- size_t i1;
- UInt8 sum = 0;
- UInt8 broadcast_sum = 0xFF;
-
- for (i1 = 0; i1 < 6; i1++) {
- sum |= addr[i1] ^ hdr[i1];
- broadcast_sum &= hdr[i1];
- }
- if (sum != 0 && broadcast_sum != 0xff) {
- freemsg(mp);
- return;
- }
- }
-
- /* Make sure we have at least one interested party. */
- bind = dle->dle_bind_list;
- do {
- if ((bind->bind_flags & (F_BIND_802 | F_BIND_SNAP)) == F_BIND_802)
- break;
- } while ((bind = bind->bind_next) != nilp(bind_t));
- if (bind == nilp(bind_t)) {
- freemsg(mp);
- return;
- }
-
- /* Allocate a DL_UNITDATA_IND to pass upstream. */
- mp1 = allocb(sizeof(dl_unitdata_ind_t)+16, BPRI_LO);
- if (mp1 == nilp(mblk_t)) {
- freemsg(mp);
- return;
- }
- mp1->b_cont = mp;
- mp = mp1;
- mp->b_datap->db_type = M_PROTO;
-
- dst = mp->b_rptr;
- dlui = (dl_unitdata_ind_t *)dst;
-
- sum = hdr[0];
- if (sum & 0x1) {
- if ((sum & hdr[1] & hdr[2] & hdr[3] & hdr[4] & hdr[5]) == 0xFF) {
- dle->dle_istatus.broadcast_frames_received++;
- dlui->dl_group_address = keaBroadcast;
- } else {
- dle->dle_istatus.multicast_frames_received++;
- dlui->dl_group_address = keaMulticast;
- }
- } else {
- dlui->dl_group_address = 0;
- dle->dle_istatus.unicast_frames_received++;
- }
-
- dlui->dl_primitive = DL_UNITDATA_IND;
- dlui->dl_dest_addr_length = 8;
- dlui->dl_dest_addr_offset = sizeof(*dlui);
- dlui->dl_src_addr_offset = sizeof(*dlui) + 8;
- dlui->dl_src_addr_length = 8;
-
- dst = (UInt8 *)&dlui[1];
- dst[0] = hdr[0];
- dst[1] = hdr[1];
- dst[2] = hdr[2];
- dst[3] = hdr[3];
- dst[4] = hdr[4];
- dst[5] = hdr[5];
- dst[6] = 0;
- dst[7] = hdr[LLC_DSAP_OFFSET];
- dst += 8;
-
- dst[0] = hdr[6];
- dst[1] = hdr[7];
- dst[2] = hdr[8];
- dst[3] = hdr[9];
- dst[4] = hdr[10];
- dst[5] = hdr[11];
- dst[6] = 0;
- dst[7] = hdr[LLC_SSAP_OFFSET];
-
- dst += 8;
- mp->b_wptr = dst;
-
- do {
- /*
- * Pass broadcast sap packets only to plain 802 bindings,
- * not to SNAP bindings. We have to check for correct
- * flags from the first bind, since dle_inbound_probe
- * only checks for the first 802 stream and does not
- * qualify on SNAP status. This allows XID and Test
- * packets to be dispatched properly for the global sap.
- */
- if ((bind->bind_flags & (F_BIND_802 | F_BIND_SNAP)) != F_BIND_802) {
- bind = bind->bind_next;
- continue;
- }
-
- mp1 = dupmsg(mp);
- if ( mp1 != nilp(mblk_t) ) {
- putq(bind->bind_dcl->dcl_rq, mp1);
- } else {
- putq(bind->bind_dcl->dcl_rq, mp);
- mp = nilp(mblk_t);
- break;
- }
- } while ( bind != nilp(bind_t) );
-
- if ( mp != nilp(mblk_t) )
- freemsg(mp);
- }
-
- void
- dle_inbound (dle_t * dle, mblk_t * mp)
- {
- UInt8 * rptr = mp->b_rptr;
- UInt32 hash_sap = (((UInt32)rptr[12]) << 8) | (UInt32)rptr[13];
- UInt32 ssap;
- UInt32 dsap;
- bind_t * bind;
- dcl_t * dcl;
- UInt32 dest_addr_type;
- UInt32 flags = 0;
- UInt32 match_flags = 0;
- size_t hdr_len;
- size_t i1;
- size_t msg_len;
- mblk_t * mp1;
- UInt8 * snap_ptr = nilp(UInt8);
- UInt8 sum;
-
- msg_len = 0;
- mp1 = mp;
- do {
- msg_len += mp1->b_wptr - mp1->b_rptr;
- } while ( (mp1 = mp1->b_cont) != nilp(mblk_t) );
-
- dle->dle_intr_active = true;
-
- /* Interface MIB statistics. */
- dle->dle_istatus.bytes_received += msg_len;
-
- dsap = hash_sap;
- ssap = hash_sap;
- hdr_len = 14;
- /* Determine what type of binding we are looking for. */
- if (hash_sap < kMinDIXSAP) {
- /*
- * Make sure that the length in the 802.3 header matches
- * the number of bytes actually received. Note that hash_sap
- * actually holds the length of the packet at this point.
- */
- hash_sap += 14;
- if (hash_sap != msg_len) {
- int xtra_bytes;
-
- if (hash_sap > msg_len) {
- /* Not enough bytes in the message. */
- dle_inbound_error(dle, mp, DL_BAD_802_3_LENGTH);
- dle->dle_intr_active = 0;
- return;
- }
- /* Strip extra pad bytes from the end of the packet. */
- xtra_bytes = msg_len - hash_sap;
- if (mp->b_cont != nilp(mblk_t))
- adjmsg(mp, -xtra_bytes);
- else
- mp->b_wptr -= xtra_bytes;
- msg_len = hash_sap;
- }
-
- dsap = (UInt32)rptr[LLC_DSAP_OFFSET];
- ssap = (UInt32)rptr[LLC_SSAP_OFFSET];
- hash_sap = dsap;
-
- /*
- * If there are two bytes of 0xFF at offsets 14 and 15, then we
- * have an IPX "raw" packet. If there is only one byte of 0xFF
- * at offset 14, then we have a packet destined for the 802
- * broadcast sap.
- */
- if ((dsap & ssap) == 0xff) {
- flags = F_BIND_IPX;
- goto check_hash_table;
- }
-
- flags = F_BIND_802;
- match_flags = F_BIND_MATCH_ANY_802;
-
- hdr_len = 17;
- /* Catch 802.2 Test and XID packets. */
- if (rptr[LLC_CONTROL_OFFSET] != LLC_DATA_VALUE) {
- /*
- * dle_inbound_xidtest will ignore any packets it
- * is not interested in.
- */
- mp1 = dupmsg(mp);
- if (mp1 != nilp(mblk_t))
- dle_inbound_xidtest(dle, mp1);
- goto check_promiscuous;
- }
-
- switch (dsap) {
- case 0xAA:
- hdr_len = 22;
- snap_ptr = &rptr[17];
- flags = F_BIND_802 | F_BIND_SNAP;
- hash_sap = ((UInt32)rptr[20] << 8) | (UInt32)rptr[21];
- break;
- case 0xFF:
- /*
- * If there's only one byte of 0xFF, then we
- * have a broadcast sap for 802.
- */
- mp1 = dupmsg(mp);
- if (mp1 != nilp(mblk_t))
- dle_inbound_802_broadcast(dle, mp);
- goto check_promiscuous;
- default:
- break;
- }
-
- /*
- * Make sure we have at least enough data to hold the headers.
- * This test must follow the XID/Test checks.
- */
- if (msg_len < hdr_len) {
- freemsg(mp);
- dle->dle_intr_active = 0;
- return;
- }
- }
-
- check_hash_table:;
- /* Find the beginning of the appropriate row in the hash table. */
- i1 = DLE_SAP_HASH_VALUE(hash_sap, flags);
- bind = dle->dle_sap_hash_tbl[i1];
-
- /* Walk thru all binds in this chain looking for the destination sap. */
- while (bind != nilp(bind_t)) {
- if (bind->bind_sap == hash_sap)
- goto process_packet;
-
- /*
- * We haven't found the sap list yet, so walk along
- * the hash chain.
- */
- bind = bind->bind_hash_next;
- }
-
- check_promiscuous:;
- dle->dle_istatus.receive_unknown_protos++;
-
- /*
- * If we didn't match any specific binds, then check for
- * promiscuous bindings.
- */
- if ((rptr[0] & 0x1) && (bind = dle->dle_match_any_multicast) != nilp(bind_t))
- goto process_packet;
-
- if ((flags & F_BIND_802) && (bind = dle->dle_match_any_802) != nilp(bind_t))
- goto process_packet;
-
- bind = dle->dle_match_any;
- if (bind == nilp(bind_t)) {
- freemsg(mp);
- dle->dle_intr_active = 0;
- return;
- }
-
- process_packet:;
- mp->b_rptr = &rptr[hdr_len]; /* strip header for M_DATA folk */
-
- match_flags |= F_BIND_MATCH_ANY;
- sum = rptr[0];
- if (sum & 0x1) {
- match_flags |= F_BIND_MATCH_ANY_MULTICAST;
- if ((sum & rptr[1] & rptr[2] & rptr[3] & rptr[4] & rptr[5]) == 0xFF) {
- dle->dle_istatus.broadcast_frames_received++;
- dest_addr_type = keaBroadcast;
- } else {
- dle->dle_istatus.multicast_frames_received++;
- dest_addr_type = keaMulticast;
- }
- } else {
- dest_addr_type = 0;
- dle->dle_istatus.unicast_frames_received++;
- }
-
- /*
- * Loop through all binds for the target sap, and then loop through
- * all relevant promiscuous binds. The packet is delivered to each
- * interested party.
- */
- do {
- bind_t * next = bind->bind_sap_next;
- UInt32 flags = bind->bind_flags;
-
- /*
- * Make sure the destination address is targetted for this
- * binding.
- */
- {
- /*
- * Match against our local address before checking for other
- * multicast addresses that are associated with this stream.
- */
- UInt8 * addr = dle->dle_current_addr;
- dle_addr_t * dlea = bind->bind_dcl->dcl_addr_list;
-
- while (true) {
- sum = addr[0] ^ rptr[0];
- sum |= addr[1] ^ rptr[1];
- sum |= addr[2] ^ rptr[2];
- sum |= addr[3] ^ rptr[3];
- sum |= addr[4] ^ rptr[4];
- sum |= addr[5] ^ rptr[5];
- if (sum == 0)
- goto find_next;
- if (dlea == nilp(dle_addr_t))
- break;
- addr = dlea->dlea_addr;
- dlea = dlea->dlea_next;
- }
- }
-
- /* If this binding is promiscuous, then deliver the packet. */
- if (bind->bind_flags & F_BIND_PROMISCUOUS_MASK) {
- /* Check for binds which only want multicast addresses.*/
- if ((bind->bind_flags & F_BIND_MATCH_ANY_MULTICAST)
- && (dest_addr_type == 0))
- bind = nilp(bind_t);
- } else if (dest_addr_type != keaBroadcast) {
- /*
- * If we didn't find a matching address setting,
- * then walk to the next bind, if there is one.
- */
- bind = nilp(bind_t);
- }
-
- find_next:;
- match_flags |= (flags & F_BIND_HASH);
-
- /*
- * If we're at the end of binds for this sap or at the end of
- * processing one of the promiscuous lists, then check for the
- * next promiscuous list to follow.
- */
- if (next == nilp(bind_t)) {
- if (flags & F_BIND_HASH) {
- /*
- * If we have a sap match and there are clients
- * wanting to see all matched packets, then the
- * first promiscuous list to check is
- * dle_match_matched.
- */
- if ((match_flags & F_BIND_HASH)
- && (next = dle->dle_match_matched) != nilp(bind_t))
- goto found_next;
- goto check_multicast_promiscuous;
- }
-
- /*
- * We're already following the promiscuous list,
- * dle_match_any, so there are no more lists to check
- * for interested clients. Note that this check must
- * follow the check for F_BIND_HASH since both flags
- * may be set for promiscuous sap bindings.
- */
- if (flags & F_BIND_MATCH_ANY)
- goto found_next;
-
- /*
- * We're following dle_match_matched list and now we
- * need to switch to dle_match_any_multicast, if the
- * destination is a multicast address.
- */
- if (flags & F_BIND_MATCH_MATCHED) {
- check_multicast_promiscuous:;
- /*
- * If the destination is a multicast address
- * and there are promiscuous multicast bound
- * clients, then the next list to use is
- * dle_match_any_multicast.
- */
- if ((match_flags & F_BIND_MATCH_ANY_MULTICAST)
- && (next = dle->dle_match_any_multicast) != nilp(bind_t))
- goto found_next;
- goto check_802_promiscuous;
- }
-
- /*
- * We're following dle_match_any_multicast list and
- * now we need to switch to dle_match_any_802, if this
- * is an 802 packet.
- */
- if (flags & F_BIND_MATCH_ANY_MULTICAST) {
- check_802_promiscuous:;
- /*
- * If this is an 802 packet and there are
- * promiscuous 802 bound clients (OT private),
- * then the next list is dle_match_any_802.
- */
- if ((match_flags & F_BIND_MATCH_ANY_802)
- && (next = dle->dle_match_any_802) != nilp(bind_t))
- goto found_next;
- }
-
- /*
- * If we've checked all other promiscuous lists, then
- * it's finally time to try the last one for matching
- * all packets.
- */
- next = dle->dle_match_any;
- }
- found_next:;
- if (bind == nilp(bind_t)) {
- bind = next;
- continue;
- }
-
- /* Deliver the packet to the client stream of this bind. */
- dcl = bind->bind_dcl;
- if (!(dcl->dcl_flags & F_DCL_WANTS_RS_HEADER)
- && ((match_flags & F_BIND_MATCH_ANY_MULTICAST)
- | (dcl->dcl_flags & F_DCL_NOT_FAST_PATH))) {
- if (mp->b_datap->db_type != M_PROTO) {
- size_t addr_len = 8;
- dl_unitdata_ind_t * dlui;
- UInt8 * dst, * hdr;
-
- if (snap_ptr != nilp(UInt8))
- addr_len = 13;
- mp1 = allocb(sizeof(dl_unitdata_ind_t)+(2*addr_len), BPRI_LO);
- if (mp1 == nilp(mblk_t)) {
- bind = next;
- if (bind != nilp(bind_t))
- continue;
- freemsg(mp);
- dle->dle_intr_active = 0;
- return;
- }
- mp1->b_cont = mp;
- mp = mp1;
- mp->b_datap->db_type = M_PROTO;
-
- dst = mp->b_rptr;
- dlui = (dl_unitdata_ind_t *)dst;
- dlui->dl_primitive = DL_UNITDATA_IND;
- dlui->dl_dest_addr_length = addr_len;
- dlui->dl_dest_addr_offset = sizeof(*dlui);
- dlui->dl_src_addr_offset = sizeof(*dlui) + addr_len;
- dlui->dl_src_addr_length = addr_len;
-
- hdr = rptr;
- dlui->dl_group_address = dest_addr_type;
-
- dst = (UInt8 *)&dlui[1];
- dst[0] = hdr[0];
- dst[1] = hdr[1];
- dst[2] = hdr[2];
- dst[3] = hdr[3];
- dst[4] = hdr[4];
- dst[5] = hdr[5];
- dst[6] = (UInt8)(dsap >> 8);
- dst[7] = (UInt8)dsap;
- if (snap_ptr != nilp(UInt8)) {
- dst[8] = snap_ptr[0];
- dst[9] = snap_ptr[1];
- dst[10] = snap_ptr[2];
- dst[11] = snap_ptr[3];
- dst[12] = snap_ptr[4];
- }
- dst += addr_len;
-
- dst[0] = hdr[6];
- dst[1] = hdr[7];
- dst[2] = hdr[8];
- dst[3] = hdr[9];
- dst[4] = hdr[10];
- dst[5] = hdr[11];
- dst[6] = (UInt8)(ssap >> 8);
- dst[7] = (UInt8)ssap;
- if (snap_ptr != nilp(UInt8)) {
- dst[8] = snap_ptr[0];
- dst[9] = snap_ptr[1];
- dst[10] = snap_ptr[2];
- dst[11] = snap_ptr[3];
- dst[12] = snap_ptr[4];
- }
- dst += addr_len;
- mp->b_wptr = dst;
- }
- mp1 = mp;
- if (next != nilp(bind_t))
- mp1 = dupmsg(mp);
- } else if ( mp->b_datap->db_type == M_PROTO) {
- if (next != nilp(bind_t)) {
- mp1 = dupmsg(mp->b_cont);
- } else {
- mp1 = mp->b_cont;
- freeb(mp);
- }
- } else {
- mp1 = mp;
- if (next)
- mp1 = dupmsg(mp);
- }
- if (mp1 != nilp(mblk_t)) {
- if (!(dcl->dcl_flags & F_DCL_WANTS_RS_HEADER)
- || (mp1 = dle_inbound_special(dcl, mp1, msg_len, rptr, DL_NORMAL_STATUS)) != nilp(mblk_t))
- putq(dcl->dcl_rq, mp1);
- }
- bind = next;
- } while (bind != nilp(bind_t));
-
- dle->dle_intr_active = 0;
- }
-
- /*
- * dle_inbound_error is called from the board-specific receive routine to deliver
- * a corrupted packet to interested promiscuous streams. Board code should
- * only call here after we have passed an error_count > 0 to its address reset
- * routine. Nothing bad will happen if this routine is called when there are
- * no interested streams, but it's a waste of effort.
- *
- * dle_inbound also calls this routine when it receives a packet with a too large
- * 802.3 length (the length in the header is greater than the number of bytes
- * received.
- */
- void
- dle_inbound_error (dle_t * dle, mblk_t * mp, unsigned long flags)
- {
- bind_t * bind;
- bind_t * bind_next;
- mblk_t * mp1;
- unsigned int msg_len;
-
- /* See if any promiscuous bindings want error packets. */
- for (bind = dle->dle_match_any; ; bind = bind->bind_sap_next) {
- if (bind == nilp(bind_t)) {
- freemsg(mp);
- return;
- }
- if (bind->bind_dcl->dcl_flags & F_DCL_WANTS_ERROR_PACKETS)
- break;
- }
-
- /* Calculate the length of the packet. */
- msg_len = 0;
- mp1 = mp;
- do {
- msg_len += mp1->b_wptr - mp1->b_rptr;
- } while ((mp1 = mp1->b_cont) != NULL);
-
- flags |= DL_ERROR_STATUS;
- do {
- bind_next = bind->bind_sap_next;
- while (bind_next) {
- if (bind_next->bind_dcl->dcl_flags & F_DCL_WANTS_ERROR_PACKETS)
- break;
- bind_next = bind_next->bind_sap_next;
- }
-
- /* If there is more than one interested stream, then dup the message. */
- if (bind_next) {
- mp1 = dupmsg(mp);
- if (!mp1) {
- bind_next = nilp(bind_t);
- mp1 = mp;
- }
- } else
- mp1 = mp;
-
- mp1 = dle_inbound_special(bind->bind_dcl, mp1, msg_len, mp1->b_rptr, flags);
- if (mp1)
- putq(bind->bind_dcl->dcl_rq, mp1);
- } while ((bind = bind_next) != NULL);
- }
-
- /*
- * dle_inbound_special is called by dle_inbound and dle_inbound_error to prepend
- * receive status on a packet. The new message is returned to the caller for
- * delivery to the client queue.
- */
- static mblk_t *
- dle_inbound_special (dcl_t * dcl, mblk_t * mp1, size_t msg_len
- , UInt8 * rptr, UInt32 flags)
- {
- mblk_t * mp2;
- mblk_t * mp3;
- dl_recv_status_t * dlrs;
-
- /* Point mp2 at the first M_DATA mblk */
- mp2 = mp1;
- if (mp1->b_datap->db_type == M_PROTO)
- mp2 = mp1->b_cont;
-
- /* Restore b_rptr to the beginning of the full packet */
- mp2->b_rptr = rptr;
-
- /*
- * Allocate a separate block for the receive status. The message passed in
- * may be dup'ed, so it is not generally safe to overwrite the status at
- * the top of the message, even if the space is available. This code could
- * probably be made more efficient, but it would be much more complicated.
- */
- mp3 = allocb(sizeof(dl_recv_status_t), BPRI_LO);
- if (mp3 == nilp(mblk_t)) {
- freemsg(mp1);
- return nilp(mblk_t);
- }
- mp3->b_cont = mp2;
- if (mp1 == mp2)
- mp1 = mp3;
- else
- mp1->b_cont = mp3;
- mp2 = mp3;
- rptr = mp2->b_datap->db_lim;
- mp2->b_wptr = rptr;
-
- rptr -= sizeof(dl_recv_status_t);
- mp2->b_rptr = rptr;
- dlrs = (dl_recv_status_t *)rptr;
- OTGetTimeStamp(&dlrs->dl_timestamp);
- dlrs->dl_flags = DL_VERSION | flags;
- dlrs->dl_packet_length_before_truncation = msg_len;
-
- /* If the packet is longer than the truncation length, trim it. */
- if (msg_len > dcl->dcl_truncation_length) {
- dlrs->dl_flags |= DL_TRUNCATED_PACKET;
- adjmsg(mp2, dcl->dcl_truncation_length - msg_len);
- msg_len = dcl->dcl_truncation_length;
- }
-
- msg_len += sizeof(dl_recv_status_t);
- /* Pad to an eight byte boundary */
- if (msg_len & 0x7) {
- int len_needed = 8 - (msg_len & 0x7);
-
- /* Find the last mblk, so we can pad it out */
- for (mp3 = mp2; mp3->b_cont != nilp(mblk_t); mp3 = mp3->b_cont)
- ;
-
- if ((mp3->b_datap->db_lim - mp3->b_wptr) < len_needed) {
- mp3->b_cont = allocb(len_needed, BPRI_LO);
- if (mp3->b_cont == nilp(mblk_t)) {
- freemsg(mp1);
- return nilp(mblk_t);
- }
- mp3 = mp3->b_cont;
- }
- msg_len += len_needed;
- while (len_needed-- != 0)
- *mp3->b_wptr++ = 0;
- }
- dlrs->dl_overall_length = msg_len;
- return mp1;
- }
-
- /*
- * Handle incoming 802.2 Test and XID messages from dle_inbound_finish.
- *
- * If the message is a command and there are any binds requesting
- * automatic handling, then we will send one response. Additionally,
- * we pass a DL_TEST_IND/DL_XID_IND to each bind not specifying
- * automatic handling.
- *
- * For response messages, we pass a DL_TEST_CON/DL_XID_CON upstream
- * to all non-automatic binds. Nothing is done with these packets
- * for automatic binds. If there are only automatic binds, then
- * the packet will be ignored and freed.
- */
- static void
- dle_inbound_xidtest (dle_t * dle, mblk_t * mp)
- {
- bind_t * bind = nilp(bind_t);
- mblk_t * dl_mp;
- mblk_t * mp1;
- UInt8 * hdr = mp->b_rptr;
- size_t i1;
- UInt8 dsap;
- UInt32 match_flags;
- UInt32 dl_primitive;
- UInt32 auto_flag;
- Boolean responded;
-
- /* Make sure we have enough data for the LLC header. */
- if (mp->b_wptr - hdr < 17) {
- freemsg(mp);
- return;
- }
-
- /*
- * We only want to handle these packets if they
- * were sent to our local address or to a
- * broadcast address. No multicasts.
- */
- {
- UInt8 * addr = dle->dle_current_addr;
- UInt8 sum = 0;
- UInt8 broadcast_sum = 0xFF;
-
- for (i1 = 0; i1 < 6; i1++) {
- sum |= addr[i1] ^ hdr[i1];
- broadcast_sum &= hdr[i1];
- }
- if (sum != 0 && broadcast_sum != 0xff) {
- freemsg(mp);
- return;
- }
- }
-
- dsap = hdr[LLC_DSAP_OFFSET];
- switch (hdr[LLC_CONTROL_OFFSET]) {
- case LLC_TEST_VALUE:
- case LLC_TEST_VALUE | LLC_POLL_FINAL_FLAG:
- if (hdr[LLC_SSAP_OFFSET] & LLC_RESPONSE_BIT)
- dl_primitive = DL_TEST_CON;
- else
- dl_primitive = DL_TEST_IND;
- auto_flag = F_BIND_AUTO_TEST;
- break;
-
- case LLC_XID_VALUE:
- case LLC_XID_VALUE | LLC_POLL_FINAL_FLAG:
- if (hdr[LLC_SSAP_OFFSET] & LLC_RESPONSE_BIT)
- dl_primitive = DL_XID_CON;
- else
- dl_primitive = DL_XID_IND;
- auto_flag = F_BIND_AUTO_XID;
-
- /*
- * As per the 802.2 spec, data in the packet is ignored,
- * and we will only send back 3 bytes in any response.
- * Note that we do not pass the 3 bytes upstream to the
- * client.
- */
- if (mp->b_cont != nilp(mblk_t)) {
- freemsg(mp->b_cont);
- mp->b_cont = nilp(mblk_t);
- }
- mp->b_wptr = &hdr[17];
- break;
-
- default:
- freemsg(mp);
- return;
- }
-
- mp->b_rptr = hdr + 17; /* Strip Ethernet and LLC headers */
-
- bind = dle->dle_bind_list;
- switch (dsap) {
- case 0:
- /* Only take command packets. */
- if (hdr[LLC_SSAP_OFFSET] & LLC_RESPONSE_BIT)
- bind = nilp(bind_t);
- if (bind != nilp(bind_t)) {
- dle_send_xidtest_response(bind, mp);
- freemsg(mp);
- return;
- }
- break;
-
- case 0xAA:
- match_flags = F_BIND_802 | F_BIND_SNAP;
- break;
-
- case 0xFF:
- match_flags = F_BIND_802;
- break;
-
- default:
- i1 = DLE_SAP_HASH_VALUE(((UInt32)dsap), F_BIND_802);
- bind = dle->dle_sap_hash_tbl[i1];
- match_flags = 0;
- while (bind != nilp(bind_t) && bind->bind_sap != dsap)
- bind = bind->bind_hash_next;
- break;
- }
-
- if (bind == nilp(bind_t)) {
- freemsg(mp);
- return;
- }
-
- responded = 0;
- dl_mp = nilp(mblk_t);
-
- /* Loop through all binds for this sap. */
- while (bind != nilp(bind_t)) {
- if ((bind->bind_flags & match_flags) != match_flags)
- goto next_one;
-
- if (bind->bind_flags & auto_flag ) {
- /*
- * If this is a command and we have not done
- * an automatic response yet, then send back a
- * response. If the packet is a response, then
- * just ignore it.
- *
- * For commands sent to the Global/Broadcast sap,
- * we will respond once for each unique binding.
- */
- if (!(hdr[LLC_SSAP_OFFSET] & LLC_RESPONSE_BIT)) {
- if (dsap == 0xFF) {
- /*
- * We only want to respond once for all
- * SNAP bindings.
- */
- if (bind->bind_flags & F_BIND_SNAP) {
- if (responded)
- goto next_one;
- responded = 1;
- }
- dle_send_xidtest_response(bind, mp);
- } else if ( !responded ) {
- dle_send_xidtest_response(bind, mp);
- responded = 1;
- }
- }
- goto next_one;
- }
-
- /*
- * Create a DLPI message block if we have not done so
- * already. These messages have the same format with
- * different dl_primitives; this makes it easy to use
- * common code for creating them.
- */
- if (dl_mp == nilp(mblk_t)) {
- dl_test_con_t * dltc;
- UInt8 * dst;
-
- dl_mp = allocb(sizeof(dl_test_con_t) + 16, BPRI_MED);
- if (dl_mp == nilp(mblk_t)) {
- freemsg(mp);
- return;
- }
- dl_mp->b_datap->db_type = M_PROTO;
- dst = dl_mp->b_rptr;
- dl_mp->b_wptr = &dst[sizeof(dl_test_con_t) + 16];
-
- dltc = (dl_test_con_t *)dst;
- dltc->dl_primitive = dl_primitive;
-
- if (hdr[LLC_CONTROL_OFFSET] & LLC_POLL_FINAL_FLAG)
- dltc->dl_flag = DL_POLL_FINAL;
- else
- dltc->dl_flag = 0;
- dltc->dl_dest_addr_length = 8;
- dltc->dl_dest_addr_offset = sizeof(dl_test_con_t);
- dltc->dl_src_addr_length = 8;
- dltc->dl_src_addr_offset = sizeof(dl_test_con_t) + 8;
-
- dst = (UInt8 *)&dltc[1];
- dst[0] = hdr[0];
- dst[1] = hdr[1];
- dst[2] = hdr[2];
- dst[3] = hdr[3];
- dst[4] = hdr[4];
- dst[5] = hdr[5];
- dst[6] = 0;
- dst[7] = dsap;
-
- dst[8] = hdr[6];
- dst[9] = hdr[7];
- dst[10] = hdr[8];
- dst[11] = hdr[9];
- dst[12] = hdr[10];
- dst[13] = hdr[11];
- dst[14] = 0;
- dst[15] = hdr[LLC_SSAP_OFFSET] & ~LLC_RESPONSE_BIT;
-
- /*
- * If there is data in the message, then
- * pass the M_DATA blocks upstream. Note that this
- * only happens with Test messages. Any data in
- * XID messages has already been stripped.
- */
- if (mp->b_rptr != mp->b_wptr)
- dl_mp->b_cont = dupmsg(mp);
- else if (mp->b_cont != nilp(mblk_t))
- dl_mp->b_cont = dupmsg(mp->b_cont);
- }
-
- mp1 = dupmsg(dl_mp);
- if (mp1 == nilp(mblk_t)) {
- mp1 = dl_mp;
- dl_mp = nilp(mblk_t);
- }
- putq(bind->bind_dcl->dcl_rq, mp1);
-
- next_one:;
- if (match_flags == 0)
- bind = bind->bind_sap_next;
- else
- bind = bind->bind_next;
- }
-
- /* If there is a dangling dupmsg, then free the extra message. */
- if (dl_mp != nilp(mblk_t))
- freemsg(dl_mp);
- freemsg(mp);
- }
-
- /* Handle DL_INFO_REQ messages for dle_wput. */
- static int
- dle_info_req (queue_t * q, mblk_t * mp)
- {
- dl_info_ack_t * dlia = (dl_info_ack_t *)mp->b_rptr;
- dcl_t * dcl = (dcl_t *)q->q_ptr;
- dle_t * dle = dcl_to_dle(dcl);
- UInt8 * dst;
-
- /* Make sure any fields we don't know about are zero'ed. */
- bzero((UInt8 *)dlia, sizeof(dl_info_ack_t));
-
- mp->b_datap->db_type = M_PCPROTO;
- dlia->dl_primitive = DL_INFO_ACK;
-
- /*
- * Maximum packet size, including headers. The appropriate header
- * length is subtracted below, depending on how the stream is bound.
- */
- dlia->dl_max_sdu = dle->dle_istatus.mtu;
- dlia->dl_min_sdu = dle->dle_min_sdu;
-
- dlia->dl_service_mode = DL_CLDLS;
- dlia->dl_provider_style = DL_STYLE1;
- dlia->dl_version = DL_VERSION_2;
- dlia->dl_current_state = dcl->dcl_state;
- dlia->dl_mac_type = dcl->dcl_mac_type;
-
- /*
- * Return the current Ethernet address. If we're not bound, then DLPI
- * says that we should return dl_addr_length 0; however, this tends
- * to mess up protocols, so we always return the Ethernet address.
- */
- dlia->dl_addr_offset = sizeof(dl_info_ack_t);
- dst = (UInt8 *)&dlia[1];
- bcopy(dle->dle_current_addr, dst, 6);
-
- if (dlia->dl_current_state == DL_IDLE) {
- /*
- * If we're bound, then return the sap information.
- * Sap length is -2 after bind, -7 after subsequent bind.
- */
- if (dcl->dcl_flags & F_DCL_SNAP) {
- dlia->dl_sap_length = -7; /* must be -7 */
- dlia->dl_addr_length = 13; /* must be 13 */
- dst[6] = 0xaa;
- dst[7] = 0xaa;
- bcopy(dcl->dcl_snap, &dst[8], 5);
- dlia->dl_max_sdu -= 22; /* Full SNAP header */
- } else {
- dlia->dl_sap_length = -2;
- dlia->dl_addr_length = 8;
- dst[6] = dcl->dcl_sap >> 8;
- dst[7] = dcl->dcl_sap & 0xff;
- if (dcl->dcl_flags & F_DCL_802)
- dlia->dl_max_sdu -= 17; /* 802 header size */
- else
- dlia->dl_max_sdu -= 14; /* Ethernet header size*/
- }
- } else {
- /* If we're not bound, then just return the Ethernet address. */
- dlia->dl_sap_length = 0;
- dlia->dl_addr_length = 6;
- dlia->dl_max_sdu -= 14; /* Default header size */
- }
-
- dst += dlia->dl_addr_length;
- dlia->dl_brdcst_addr_offset = sizeof(dl_info_ack_t) + dlia->dl_addr_length;
- dlia->dl_brdcst_addr_length = 6;
- dst[0] = 0xFF;
- dst[1] = 0xFF;
- dst[2] = 0xFF;
- dst[3] = 0xFF;
- dst[4] = 0xFF;
- dst[5] = 0xFF;
- mp->b_wptr = &dst[6]; /* past broadcast address */
-
- qreply(q, mp);
- return 0;
- }
-
- /*
- * Make a new board available to the dlpiether common code. This routine is
- * called by the board-specific code during initialization; the dle may be
- * allocated either statically or dynamically by the board code. After
- * dle_init, streams for the specified board may be opened by applications.
- */
- void
- dle_init (dle_t * dle, size_t xtra_hdr_len)
- {
- /*
- * Initialize the static parts of the interface statistics and dle
- * information. The board code can override these values if necessary.
- */
- dle->dle_istatus.speed = 10000000; /* in bits per second */
-
- /*
- * Set the max transmit size to Ethernet default, including headers.
- * dle_info_req subtracts the appropriate header length before telling
- * upstream clients what size they may use. For "normal" Ethernet,
- * the reported size is 1500, for 802.2 SNAP, the size is 1492.
- */
- dle->dle_istatus.mtu = 1514;
-
- /* The min transmit size is 0 since we can pad to the real 60-byte min. */
- dle->dle_min_sdu = 0;
-
- dle->dle_xtra_hdr_len = xtra_hdr_len;
- }
-
- /*
- * Find the specified address in the list of addresses for this dcl and return
- * a pointer to the link pointing to the appropriate structure. If the
- * address is not in the list, then the returned pointer points to the location
- * in which a new structure may be added to the list. This routine is used
- * by dle_enabmulti_req and dle_disabmulti_req to add and delete multicast
- * addresses.
- */
- static dle_addr_t **
- dle_dleap_from_addr (dcl_t * dcl, UInt8 * addr)
- {
- dle_addr_t ** dleap = (dle_addr_t **)&dcl->dcl_addr_list;
- dle_addr_t * dlea;
-
- for ( ; (dlea = dleap[0]) != nilp(dle_addr_t); dleap = &dlea->dlea_next) {
- UInt8 sum = 0;
- size_t u1;
- for (u1 = 0; u1 < 6; u1++)
- sum |= dlea->dlea_addr[u1] ^ addr[u1];
- if (sum == 0)
- break;
- }
- return dleap;
- }
-
- /*
- * STREAMS open entry point. This routine is called by the board-specific open
- * code to initialize the necessary structures.
- */
- int
- dle_open (dle_t * dle, queue_t * q, dev_t * devp, int flag, int sflag
- , cred_t * credp, size_t dcl_len)
- {
- dcl_t * dcl;
- int err;
-
- /* If this is a reopen, then there's nothing to do. */
- if (q->q_ptr != NULL) {
- mi_qprocson(q);
- return 0;
- }
-
- err = mi_open_comm(&dle->dle_instance_head, dcl_len, q, devp, flag, sflag, credp);
- if (err != 0)
- return err;
-
- dcl = (dcl_t *)q->q_ptr;
- dcl->dcl_hw = dle;
- dcl->dcl_rq = q;
- dcl->dcl_state = DL_UNBOUND;
- dcl->dcl_mac_type = DL_ETHER;
- dcl->dcl_flags = F_DCL_NOT_FAST_PATH;
- dle->dle_refcnt++;
- return 0;
- }
-
- /* Handle DL_PHYS_ADDR_REQ messages from dle_wput. */
- static int
- dle_phys_addr_req (queue_t * q, mblk_t * mp)
- {
- UInt8 * rptr = mp->b_rptr;
- UInt8 * addr;
- dl_phys_addr_req_t * dpar;
- dl_phys_addr_ack_t * dpaa;
- dle_t * dle;
-
- rptr = mp->b_rptr;
- dpar = (dl_phys_addr_req_t *)rptr;
- dle = dcl_to_dle((dcl_t *)q->q_ptr);
- switch (dpar->dl_addr_type) {
- case DL_CURR_PHYS_ADDR:
- addr = &dle->dle_current_addr[0];
- break;
- case DL_FACT_PHYS_ADDR:
- addr = &dle->dle_factory_addr[0];
- break;
- default:
- return DL_BADPRIM;
- }
- dpaa = (dl_phys_addr_ack_t *)rptr;
- dpaa->dl_primitive = DL_PHYS_ADDR_ACK;
- dpaa->dl_addr_length = 6;
- dpaa->dl_addr_offset = sizeof(*dpaa);
- mp->b_wptr = &rptr[sizeof(*dpaa) + 6];
- mp->b_datap->db_type = M_PCPROTO;
- bcopy(addr, &rptr[sizeof(*dpaa)], 6);
- qreply(q, mp);
- return 0;
- }
-
- /* Process DL_PROMISCOFF_REQ messages for dle_wput. */
- static int
- dle_promiscoff_req (queue_t * q, mblk_t * mp)
- {
- bind_t * bind;
- dcl_t * dcl;
- dle_t * dle;
- UInt32 flags;
- UInt8 * rptr = mp->b_rptr;
- dl_promiscoff_req_t * dlpr;
-
- dcl = (dcl_t *)q->q_ptr;
- dle = dcl_to_dle(dcl);
- dlpr = (dl_promiscoff_req_t *)rptr;
- switch (dlpr->dl_level) {
- case DL_PROMISC_PHYS:
- if (!(dcl->dcl_flags & F_DCL_PROMISC_PHYS))
- return 0;
-
- /*
- * If the stream is bound, then undo the promiscuous bits
- * on its binds.
- */
- if (dcl->dcl_state == DL_IDLE) {
- bind = (bind_t *)dle->dle_bind_list;
- while (bind != nilp(bind_t)) {
- if (bind->bind_dcl == dcl && (bind->bind_flags & F_BIND_MATCH_ANY)) {
- bind->bind_flags &= ~F_BIND_MATCH_ANY;
- dle->dle_match_any_count--;
- }
- bind = bind->bind_next;
- }
-
- /* Set the hardware into the proper state. */
- if (dle->dle_match_any_count == 0) {
- dle_hw_address_filter_reset(dle);
- }
- } else {
- flags = F_BIND_MATCH_ANY;
- }
-
- dcl->dcl_flags ^= F_DCL_PROMISC_PHYS;
- break;
- case DL_PROMISC_MULTI:
- if (!(dcl->dcl_flags & F_DCL_PROMISC_MULTI))
- return 0;
-
- /*
- * If the stream is bound, then undo the promiscuous bits
- * on its binds.
- */
- if (dcl->dcl_state == DL_IDLE) {
- bind = (bind_t *)dle->dle_bind_list;
- while (bind != nilp(bind_t)) {
- if (bind->bind_dcl == dcl && (bind->bind_flags & F_BIND_MATCH_ANY_MULTICAST)) {
- bind->bind_flags &= ~F_BIND_MATCH_ANY_MULTICAST;
- dle->dle_match_any_multicast_count--;
- }
- bind = bind->bind_next;
- }
-
- /* Set the hardware into the proper state. */
- if (dle->dle_match_any_multicast_count == 0) {
- dle_hw_address_filter_reset(dle);
- }
- } else
- flags = F_BIND_MATCH_ANY_MULTICAST;
-
- dcl->dcl_flags ^= F_DCL_PROMISC_MULTI;
- break;
- case DL_PROMISC_SAP:
- if (!(dcl->dcl_flags & F_DCL_PROMISC_SAP))
- return 0;
-
- dcl->dcl_flags ^= F_DCL_PROMISC_SAP;
- flags = F_BIND_MATCH_MATCHED;
- break;
- }
-
- if (dcl->dcl_state != DL_IDLE) {
- /* Find the bind structure in the binding lists and remove it. */
- bind = dle_bind_from_stuff(dcl, flags, dcl->dcl_sap);
- if (bind != nilp(bind_t)) {
- dle_bind_disable(dle, bind);
- dle_bind_free(dle, bind);
- }
- }
- /*
- * Flush all inbound messages pending on the stream. It is important
- * that this message go upstream before the DL_OK_ACK.
- */
- flushq(RD(q), FLUSHDATA);
- putnextctl1(RD(q), M_FLUSH, FLUSHR);
- return 0;
- }
-
- /* Process DL_PROMISCON_REQ messages for dle_wput. */
- static int
- dle_promiscon_req (queue_t * q, mblk_t * mp)
- {
- bind_t * bind;
- dcl_t * dcl;
- dle_t * dle;
- UInt32 flags;
- UInt8 * rptr = mp->b_rptr;
- dl_promiscon_req_t * dlpr;
- UInt32 sap;
-
- dcl = (dcl_t *)q->q_ptr;
- dle = dcl_to_dle(dcl);
-
- dlpr = (dl_promiscon_req_t *)rptr;
- sap = ~0;
- if (dcl->dcl_state == DL_IDLE)
- sap = dcl->dcl_sap;
-
- switch (dlpr->dl_level) {
- case DL_PROMISC_PHYS:
- if (dcl->dcl_flags & F_DCL_PROMISC_PHYS)
- return 0;
-
- /*
- * If the stream is bound to a sap, then this new bind
- * is promiscuous only for saps already bound to the
- * stream, whether bound by a DL_BIND_REQ or DL_SUBS_BIND_REQ
- * with DL_PEER_BIND. Inbound packets will be dispatched
- * with the normal hash mechanisms in dle_inbound_probe
- * and dle_inbound_finish, but the destination address will
- * not be checked. If an affected sap is unbound before
- * this setting is disabled, dle_bind_disable take the right
- * steps to undo the hardware effects.
- */
- if (dcl->dcl_state == DL_IDLE) {
- UInt32 prev_count = dle->dle_match_any_count;
-
- bind = (bind_t *)dle->dle_bind_list;
- while (bind != nilp(bind_t)) {
- if (bind->bind_dcl == dcl) {
- bind->bind_flags |= F_BIND_MATCH_ANY;
- dle->dle_match_any_count++;
- }
- bind = bind->bind_next;
- }
-
- /* Set the hardware into the proper state. */
- if (prev_count == 0 && dle->dle_match_any_count > 0) {
- dle_hw_address_filter_reset(dle);
- }
- } else {
- flags = F_BIND_MATCH_ANY;
- }
- break;
-
- case DL_PROMISC_SAP:
- if (dcl->dcl_flags & F_DCL_PROMISC_SAP)
- return 0;
-
- /*
- * If we're already bound, then this request says we want all
- * packets for this sap on this machine. There's nothing
- * more to do.
- */
- if (dcl->dcl_state == DL_IDLE)
- return 0;
-
- flags = F_BIND_MATCH_MATCHED;
- break;
- case DL_PROMISC_MULTI:
- if (dcl->dcl_flags & F_DCL_PROMISC_MULTI)
- return 0;
-
- /*
- * If the stream is bound to a sap, then this new bind
- * is promiscuous only for saps already bound to the
- * stream, whether bound by a DL_BIND_REQ or DL_SUBS_BIND_REQ
- * with DL_PEER_BIND.
- */
- if (dcl->dcl_state == DL_IDLE) {
- UInt32 prev_count = dle->dle_match_any_multicast_count;
-
- bind = (bind_t *)dle->dle_bind_list;
- while (bind != nilp(bind_t)) {
- if (bind->bind_dcl == dcl) {
- bind->bind_flags |= F_BIND_MATCH_ANY_MULTICAST;
- dle->dle_match_any_multicast_count++;
- }
- bind = bind->bind_next;
- }
-
- /* Set the hardware into the proper state. */
- if (prev_count == 0 && dle->dle_match_any_multicast_count > 0) {
- dle_hw_address_filter_reset(dle);
- }
- } else
- flags = F_BIND_MATCH_ANY_MULTICAST;
- break;
- default:
- return DL_BADPRIM;
- }
-
- if (dcl->dcl_state != DL_IDLE) {
- /*
- * If we're not bound to a particular sap, then we need
- * to create a new binding in one of the promiscuous lists.
- */
- bind = dle_bind_alloc(dcl, sap, flags);
- if (bind == nilp(bind_t))
- return DL_SYSERR;
- dle_bind_enable(bind);
- }
-
- switch (dlpr->dl_level) {
- case DL_PROMISC_PHYS:
- dcl->dcl_flags |= F_DCL_PROMISC_PHYS;
- break;
- case DL_PROMISC_MULTI:
- dcl->dcl_flags |= F_DCL_PROMISC_MULTI;
- break;
- case DL_PROMISC_SAP:
- dcl->dcl_flags |= F_DCL_PROMISC_SAP;
- break;
- }
- return 0;
- }
-
- /*
- * Remove all binds for a stream (dcl). This routine is called by
- * dle_unbind_req and dle_close.
- */
- static void
- dle_remove_all_my_binds (dcl_t * dcl)
- {
- dle_t * dle = dcl_to_dle(dcl);
- bind_t * bind;
- bind_t ** bindp;
-
- /*
- * Walk through the list of all binds for the dle and delete all
- * those associated with dcl.
- */
- for (bindp = (bind_t **)&dle->dle_bind_list; (bind = bindp[0]) != nilp(bind_t); ) {
- if (bind->bind_dcl == dcl) {
- dle_bind_disable(dle, bind);
- dle_bind_free(dle, bind);
- } else {
- bindp = &bind->bind_next;
- }
- }
- dcl->dcl_flags &= ~(F_DCL_PROMISC_PHYS | F_DCL_PROMISC_MULTI
- | F_DCL_PROMISC_SAP | F_DCL_IPX | F_DCL_SNAP | F_DCL_802);
- }
-
- void
- dle_rsrv_ctl (queue_t * q, mblk_t * mp)
- {
- mp->b_datap->db_type = M_DATA;
- put(WR(q), mp);
- }
-
- /*
- * Send an 802.2 LLC Test or XID response message in reply
- * to an inbound command packet.
- */
- static void
- dle_send_xidtest_response (bind_t * bind, mblk_t * mp)
- {
- dle_t * dle;
- mblk_t * mp1;
- UInt8 * rptr;
- UInt8 * src;
- UInt8 dsap;
-
- /*
- * We have to create our own copy of the message since we're
- * to change the contents of the packet and pass it to the
- * transmit code.
- */
- mp->b_rptr -= 17; /* Reclaim the stripped headers. */
- rptr = mp->b_rptr;
- if ((rptr[LLC_CONTROL_OFFSET] & LLC_XID_VALUE) == LLC_XID_VALUE) {
- if ((mp->b_datap->db_lim - mp->b_wptr) < 3) {
- mp1 = allocb(20, BPRI_HI);
- if (mp1 != nilp(mblk_t)) {
- bcopy(rptr, mp1->b_rptr, 17);
- mp1->b_wptr = mp1->b_rptr + 17;
- }
- } else
- mp1 = copyb(mp);
- } else {
- mp1 = copyb(mp);
- }
- mp->b_rptr += 17; /* Return the original mp unchanged. */
- if (mp1 == nilp(mblk_t))
- return;
-
- if (mp->b_cont != nilp(mblk_t)) {
- /*
- * We only have to get new references to additional
- * blocks since we're not going to alter them.
- */
- mp1->b_cont = dupmsg(mp->b_cont);
- if (mp1->b_cont == nilp(mblk_t)) {
- freeb(mp1);
- return;
- }
- }
-
- mp = mp1;
- rptr = mp->b_rptr;
- mp->b_datap->db_type = M_CTL; /* Special type for board rsrv */
- /* Set the destination address from the source address. */
- rptr[0] = rptr[6];
- rptr[1] = rptr[7];
- rptr[2] = rptr[8];
- rptr[3] = rptr[9];
- rptr[4] = rptr[10];
- rptr[5] = rptr[11];
-
- /* Set the source address. */
- dle = dcl_to_dle(bind->bind_dcl);
- src = dle->dle_current_addr;
- rptr[6] = src[0];
- rptr[7] = src[1];
- rptr[8] = src[2];
- rptr[9] = src[3];
- rptr[10] = src[4];
- rptr[11] = src[5];
-
- /*
- * Set the length field to 0 so it will be assigned correctly
- * by the board transmit code.
- */
- rptr[12] = 0;
- rptr[13] = 0;
-
- /*
- * Assign the DSAP field from the SSAP. The new SSAP field is our
- * bound sap which is guaranteed not to be a 802.2 Group sap.
- */
- dsap = rptr[LLC_DSAP_OFFSET];
- rptr[LLC_DSAP_OFFSET] = rptr[LLC_SSAP_OFFSET];
-
- if (dsap != 0) {
- if (bind->bind_flags & F_BIND_SNAP)
- rptr[LLC_SSAP_OFFSET] = 0xAA;
- else
- rptr[LLC_SSAP_OFFSET] = bind->bind_dcl->dcl_sap;
- } else
- rptr[LLC_SSAP_OFFSET] = 0;
-
- /* Turn on the response bit. */
- rptr[LLC_SSAP_OFFSET] |= LLC_RESPONSE_BIT;
-
- /* If this is an XID packet, set the necessary reply fields. */
- if ((rptr[LLC_CONTROL_OFFSET] & LLC_XID_VALUE) == LLC_XID_VALUE) {
- /* Set the XID fields. */
- rptr[17] = XID_FORMAT_IDENTIFIER;
- rptr[18] = 0x2; /* Type 2 LLC, non-NULL LSAP */
- rptr[19] = 0;
- mp->b_wptr = &rptr[20];
- }
-
- /* Use the read queue of this bind to transmit the packet. */
- putq(bind->bind_dcl->dcl_rq, mp);
- }
-
- /* Process DL_SUBSBIND_REQ messages from dle_wput. */
- static int
- dle_subs_bind_req (queue_t * q, mblk_t * mp)
- {
- UInt8 * addr;
- dl_subs_bind_ack_t * dlsba;
- dl_subs_bind_req_t * dlsbr = (dl_subs_bind_req_t *)mp->b_rptr;
- dcl_t * dcl = (dcl_t *)q->q_ptr;
- UInt32 flags;
- bind_t * bind;
- UInt32 sap;
- UInt8 * snap_ptr;
- SInt32 len; /* %%% Why signed?? */
-
- /* Make sure the stream is bound before allowing any subsequent binds. */
- if (dcl->dcl_state != DL_IDLE)
- return DL_OUTSTATE;
-
- len = dlsbr->dl_subs_sap_length;
- snap_ptr = mi_offset_param(mp, dlsbr->dl_subs_sap_offset, len);
- if (snap_ptr == nilp(UInt8))
- return DL_BADADDR;
-
- switch (dlsbr->dl_subs_bind_class) {
- case DL_HIERARCHICAL_BIND:
- /*
- * Hierarchical binds are only allowed on streams bound to the
- * SNAP sap (0xAA). Also, we only allow one subsbind per
- * stream. This lookup finds the bind structure allocated by
- * the previous DL_BIND_REQ.
- */
- flags = F_BIND_SNAP_PENDING | F_BIND_HASH;
- bind = dle_bind_from_stuff(dcl, flags, 0xAA);
- if (bind == nilp(bind_t))
- return DL_OUTSTATE;
-
- /* The requested value must contain 5 bytes of SNAP stuff. */
- if (len != 5)
- return DL_BADADDR;
-
- sap = (((UInt32)snap_ptr[3]) << 8) | (UInt32)snap_ptr[4];
-
- /*
- * Remove the bind structure from its temporary location
- * in the hash table.
- */
- dle_bind_hash_remove(dcl_to_dle(dcl), bind);
-
- /* Fix the bind structure to have its permanent information. */
- bind->bind_flags &= ~F_BIND_SNAP_PENDING;
- bind->bind_flags |= F_BIND_SNAP | F_BIND_802 | F_BIND_MATCH_LOCAL;
- bind->bind_sap = sap;
- dcl->dcl_sap = sap;
- dcl->dcl_flags |= F_DCL_SNAP;
- bcopy(snap_ptr, dcl->dcl_snap, 5);
-
- /*
- * And put the bind structure back into its new proper place
- * in the hash table. dle_bind_hash_insert is called directly
- * here because dle_bind_enable has already been called for
- * this bind by dle_bind_req.
- */
- dle_bind_hash_insert(bind);
- break;
- case DL_PEER_BIND:
- /*
- * If this stream is bound to the SNAP sap, then we do not
- * allow it to be peer-bound to additional saps. This may
- * be overly restrictive.
- */
- if (dcl->dcl_flags & F_DCL_SNAP)
- return DL_OUTSTATE;
- /* Check also to be sure that a SNAP bind is not in progress. */
- flags = F_BIND_SNAP_PENDING | F_BIND_HASH | F_BIND_MATCH_LOCAL;
- bind = dle_bind_from_stuff(dcl, flags, 0xAA);
- if (bind != nilp(bind_t))
- return DL_OUTSTATE;
-
- if (len != 2)
- return DL_BADADDR;
-
- sap = (((UInt32)snap_ptr[0]) << 8) | (UInt32)snap_ptr[1];
- flags = F_BIND_HASH | F_BIND_MATCH_LOCAL;
- if (sap == 0) {
- /*
- * Special OT promiscuous mode for receiving all
- * 802 packets.
- */
- if (!(dcl->dcl_flags & F_DCL_802))
- return DL_BADADDR;
- flags = F_BIND_MATCH_ANY_802;
- } else if (sap <= 0xFE) {
- /*
- * Make sure that other saps bound to this stream are
- * also 802 saps. We don't mix and match sap types.
- */
- if (sap == 0xAA || !(dcl->dcl_flags & F_DCL_802))
- return DL_BADADDR;
- flags |= F_BIND_802;
- } else {
- /*
- * Don't allow a non-802 sap to be bound if we're
- * already an 802 stream.
- */
- if (dcl->dcl_flags & F_DCL_802)
- return DL_BADADDR;
-
- /* Don't allow a subsbind to an IPX sap */
- if (sap == 0xFF)
- return DL_BADADDR;
-
- /* And finally make sure that the Ether type is valid. */
- if (sap > kMaxDIXSAP || sap < kMinDIXSAP)
- return DL_BADADDR;
- }
-
- /* Create a new bind structure for this sap. */
- bind = dle_bind_alloc(dcl, sap, flags);
- if (bind == nilp(bind_t))
- return DL_SYSERR;
-
- /* Propagate 802.2 status from toplevel bind. */
- if (dcl->dcl_flags & F_DCL_AUTO_TEST)
- bind->bind_flags |= F_BIND_AUTO_TEST;
- if (dcl->dcl_flags & F_DCL_AUTO_XID)
- bind->bind_flags |= F_BIND_AUTO_XID;
-
- /* Put the structure into its proper hash location. */
- dle_bind_enable(bind);
- break;
- default:
- return DL_UNSUPPORTED;
- }
-
- /*
- * Turn around the DL_SUBS_BIND_REQ as a DL_SUBS_BIND_ACK. This works
- * because the ack structure is no bigger than the request. Also note
- * that we have to copy the sap value into the right place before we
- * possibly overwrite it.
- */
- dlsba = (dl_subs_bind_ack_t *)mp->b_rptr;
- addr = (UInt8 *)&dlsba[1];
- bcopy(snap_ptr, &addr[0], len);
- dlsba->dl_primitive = DL_SUBS_BIND_ACK;
- dlsba->dl_subs_sap_offset = sizeof(*dlsba);
- dlsba->dl_subs_sap_length = len;
- mp->b_wptr = &addr[len];
- mp->b_datap->db_type = M_PCPROTO;
-
- qreply(q, mp);
- return 0;
- }
-
- /*
- * Handle DL_SUBS_UNBIND_REQ messages from dle_wput. This routine removes
- * only one binding that was created by a previous DL_SUBS_BIND_REQ. All
- * other binds on the stream are left alone.
- * dle_wput will pass a DL_OK_ACK or DL_ERROR_ACK upstream depending on the
- * return value.
- */
- static int
- dle_subs_unbind_req (queue_t * q, mblk_t * mp)
- {
- dcl_t * dcl = (dcl_t *)q->q_ptr;
- bind_t * bind;
- UInt32 flags;
- UInt32 len;
- dl_subs_unbind_req_t * dlsubr = (dl_subs_unbind_req_t *)mp->b_rptr;
- UInt32 sap;
- UInt8 * snap_ptr;
-
- if (dcl->dcl_state != DL_IDLE)
- return DL_OUTSTATE;
-
- /*
- * Verify that the DL_SUBS_UNBIND_REQ is properly formatted and get
- * a pointer to the sap (or snap) value in the message.
- */
- len = dlsubr->dl_subs_sap_length;
- snap_ptr = mi_offset_param(mp, dlsubr->dl_subs_sap_offset, len);
- if (snap_ptr == nilp(UInt8))
- return DL_BADADDR;
-
- /*
- * The length tells us if we're looking for a DL_HIERARCHICAL/SNAP
- * binding or if we're looking for a DL_PEER_BIND binding of an
- * Ethertype or 802 type.
- */
- switch (len) {
- case 5:
- /* SNAP time. */
- sap = (((UInt32)snap_ptr[3]) << 8) | (UInt32)snap_ptr[4];
- flags = F_BIND_SNAP | F_BIND_802 | F_BIND_HASH;
-
- /* Find the bind structure for this sap. */
- bind = dle_bind_from_stuff(dcl, flags, sap);
- if (bind == nilp(bind_t))
- return DL_BADADDR;
-
- /*
- * Fix the bind structure so a new DL_SUBS_BIND_REQ will succeed.
- */
- dle_bind_hash_remove(dcl_to_dle(dcl), bind);
- bind->bind_flags &= ~(F_BIND_SNAP | F_BIND_802);
- bind->bind_flags |= F_BIND_SNAP_PENDING;
- bind->bind_sap = 0xAA;
- dcl->dcl_flags &= ~F_DCL_SNAP;
- dcl->dcl_sap = 0xAA;
- dle_bind_hash_insert(bind);
- break;
- case 2:
- /* Ethertype or 802 type binding. */
- sap = (((UInt32)snap_ptr[0]) << 8) | (UInt32)snap_ptr[1];
-
- /*
- * Don't allow a subs unbind to remove the binding made
- * with a DL_BIND_REQ.
- */
- if (sap == dcl->dcl_sap)
- return DL_BADADDR;
-
- flags = F_BIND_MATCH_LOCAL | F_BIND_HASH;
- if (sap <= 0xFE)
- flags |= F_BIND_802;
-
- /* Special OT 802 promiscuous mode. */
- if (sap == 0)
- flags = F_BIND_MATCH_ANY_802;
-
- /* Find the bind structure for this sap. */
- bind = dle_bind_from_stuff(dcl, flags, sap);
- if (bind == nilp(bind_t))
- return DL_BADADDR;
- /*
- * Remove the structure from the hash table and from the dle's
- * list of binds. Note that dle_bind_disable will also clean
- * up any promiscuous modes that may have been set on the bind.
- */
- dle_bind_disable(dcl_to_dle(dcl), bind);
- dle_bind_free(dcl_to_dle(dcl), bind);
- break;
- default:
- return DL_BADADDR;
- }
- return 0;
- }
-
- /*
- * Process DL_UNBIND_REQ messages from dle_wput. This routine removes all
- * bindings on a stream, whether they were created by DL_BIND_REQ or by
- * DL_SUBS_BIND_REQ messages. dle_wput will pass a DL_OK_ACK or DL_ERROR_ACK
- * upstream depending on the return value.
- */
- static int
- dle_unbind_req (queue_t * q, mblk_t * mp)
- {
- dcl_t * dcl = (dcl_t *)q->q_ptr;
-
- /* Make sure there is at least on bind active on the stream. */
- if (dcl->dcl_state != DL_IDLE)
- return DL_OUTSTATE;
-
- /*
- * Remove and delete all entries in the hash table associated
- * with this dcl.
- */
- dle_remove_all_my_binds(dcl);
- dcl->dcl_state = DL_UNBOUND;
-
- /* Flush all pending outbound messages. */
- flushq(q, FLUSHDATA);
- /*
- * Flush all inbound messages pending on the stream. It is important
- * that this message go upstream before the DL_OK_ACK for the unbind.
- */
- flushq(RD(q), FLUSHDATA);
- putnextctl1(RD(q), M_FLUSH, FLUSHRW);
-
- return 0;
- }
-
- /*
- * Called by board code when this device is no longer available or is about to be
- * unloaded from memory (such as from its TerminateStreamModule routine). This
- * routine releases all resources that have been allocated and associated with
- * the dle by the common DLPI code.
- */
- void
- dle_terminate (dle_t * dle)
- {
- }
-
- /*
- * STREAMS write-side put procedure. This routine is called by the
- * board-specific wput routine for all messages other than M_DATAs.
- * The parameters are the q and mp passed to the board-specific wput.
- */
- mblk_t *
- dle_wput (queue_t * q, mblk_t * mp)
- {
- dcl_t * dcl;
- mblk_t * mp1;
- dblk_t * db;
- UInt8 * dst;
- UInt8 * snap_ptr;
- UInt32 dest_addr_len;
- UInt8 * hdr;
- dle_t * dle;
- UInt8 * rptr;
- dl_unitdata_req_t * dlur;
- UInt8 * src;
- UInt32 len_or_sap, hdr_len;
- UInt32 flags;
-
- dcl = (dcl_t *)q->q_ptr;
- switch (mp->b_datap->db_type) {
- case M_PROTO:
- case M_PCPROTO:
- break;
- case M_IOCTL:
- return dle_wput_ioctl(q, mp);
- case M_FLUSH:
- if (*mp->b_rptr & FLUSHW)
- flushq(q, FLUSHALL);
- if (*mp->b_rptr & FLUSHR) {
- flushq(RD(q), FLUSHALL);
- *mp->b_rptr &= ~FLUSHW;
- qreply(q, mp);
- return nilp(mblk_t);
- }
- freemsg(mp);
- return nilp(mblk_t);
- default:
- mi_strlog(q, 1, SL_TRACE | SL_ERROR,
- "dle_wput: unknown mp 0x%x db_type %d prim 0x%x\n",
- mp, mp->b_datap->db_type, *((long *)mp->b_rptr));
- freemsg(mp);
- return nilp(mblk_t);
- }
-
- /* Process all M_PROTO/M_PCPROTO DLPI messages. */
- rptr = mp->b_rptr;
- dlur = (dl_unitdata_req_t *)rptr;
- if ((mp->b_wptr - rptr) < sizeof(*dlur) || dlur->dl_primitive != DL_UNITDATA_REQ)
- return dle_wput_proto(q, mp);
-
- /*
- * Handle DL_UNITDATA_REQ messages from here. First, make sure that
- * the message is properly formatted and we have the address bytes
- * needed. Also note that from here down, the interface MIB statistics
- * are incremented in some way whether for error or for success.
- */
- dle = dcl_to_dle(dcl);
- dest_addr_len = dlur->dl_dest_addr_length;
- dst = mi_offset_param(mp, dlur->dl_dest_addr_offset, dest_addr_len);
- if (dst == nilp(UInt8)) {
- dle->dle_istatus.transmit_errors++;
- return dle_wput_ud_error(mp, DL_BADADDR, 0);
- }
-
- /* Default header length is standard Ethernet. */
- hdr_len = 14;
- /*
- * Default formatting from dcl bind flags. If the message contains sap
- * information, then that sap and requisite formatting will be used
- * instead of whatever was indicated in the bind.
- */
- flags = dcl->dcl_flags & (F_DCL_IPX | F_DCL_SNAP | F_DCL_802);
- snap_ptr = &dcl->dcl_snap[0];
-
- /* Determine formatting to be used for this packet. */
- switch (dest_addr_len) {
- case 6:
- if (flags & F_DCL_IPX) {
- len_or_sap = 0;
- break;
- }
- len_or_sap = dcl->dcl_sap;
- if (flags & F_DCL_SNAP)
- hdr_len = 22;
- else if (flags & F_DCL_802)
- hdr_len = 17;
- break;
- case 8:
- /* NOTE: for plain 802.2, only dst[7] matters, dst[6] is 0. */
- len_or_sap = (((UInt32)dst[6]) << 8) | (UInt32)dst[7];
- if (flags & F_DCL_SNAP)
- hdr_len = 22;
- else if (flags & F_DCL_802)
- hdr_len = 17;
- break;
- case 13:
- len_or_sap = (((UInt32)dst[11]) << 8) | (UInt32)dst[12];
- snap_ptr = &dst[8];
- hdr_len = 22;
- break;
- default:
- dle->dle_istatus.transmit_errors++;
- return dle_wput_ud_error(mp, DL_BADADDR, 0);
- }
-
- /*
- * Get enough space in the first mblk to hold the header, either by
- * reusing the DL_UNITDATA_REQ block or by allocating a new block.
- */
- hdr_len += dle->dle_xtra_hdr_len;
- mp1 = mp->b_cont;
- db = mp1->b_datap;
- hdr = mp1->b_rptr;
- if (db->db_type != M_DATA || db->db_ref != 1 || (hdr - db->db_base) < hdr_len) {
- db = mp->b_datap;
- if (db->db_ref == 1) {
- db->db_type = M_DATA;
- mp1 = mp;
- } else {
- mp1 = allocb(128, BPRI_LO);
- if (mp1 == nilp(mblk_t)) {
- dle->dle_istatus.transmit_discards++;
- return dle_wput_ud_error(mp, DL_SYSERR, ENOMEM);
- }
- mp1->b_cont = mp->b_cont;
- }
- hdr = mp1->b_rptr + hdr_len;
- mp1->b_wptr = hdr;
- }
-
- hdr_len -= dle->dle_xtra_hdr_len;
- hdr -= hdr_len;
- mp1->b_rptr = hdr;
-
- /* Set the destination address. */
- hdr[0] = dst[0];
- hdr[1] = dst[1];
- hdr[2] = dst[2];
- hdr[3] = dst[3];
- hdr[4] = dst[4];
- hdr[5] = dst[5];
-
- /* Set the SNAP information or simple 802 stuff, if necessary. */
- switch (hdr_len) {
- case 22:
- hdr[LLC_DSAP_OFFSET] = 0xAA;
- hdr[LLC_SSAP_OFFSET] = 0xAA;
- hdr[LLC_CONTROL_OFFSET] = LLC_DATA_VALUE;
- hdr[17] = snap_ptr[0];
- hdr[18] = snap_ptr[1];
- hdr[19] = snap_ptr[2];
- hdr[20] = (unsigned char)(len_or_sap >> 8);
- hdr[21] = (unsigned char)len_or_sap;
- len_or_sap = 0; /* For common code below */
- break;
-
- case 17:
- hdr[LLC_DSAP_OFFSET] = len_or_sap;
- hdr[LLC_SSAP_OFFSET] = dcl->dcl_sap;
- hdr[LLC_CONTROL_OFFSET] = LLC_DATA_VALUE;
- len_or_sap = 0; /* For common code below */
- break;
- }
-
- /*
- * Set either the sap or a 0 in the topmost header. If this value is
- * zero, then the board-specific code should write the actual length
- * of the packet into this area. The board code must do this because
- * fast-path M_DATA messages are not passed through dle_wput.
- */
- hdr[12] = (unsigned char)(len_or_sap >> 8);
- hdr[13] = (unsigned char)len_or_sap;
-
- /* Set the source address. */
- src = dle->dle_current_addr;
- hdr[6] = src[0];
- hdr[7] = src[1];
- hdr[8] = src[2];
- hdr[9] = src[3];
- hdr[10] = src[4];
- hdr[11] = src[5];
-
- /*
- * If we allocated a new mblk for the header, then free the
- * DL_UNITDATA_REQ message. This block cannot be deallocated until
- * the destination address and, possibly, sap/snap information is
- * copied into the header.
- */
- if (mp != mp1)
- freeb(mp);
- /*
- * b_rptr must be at start of any extra space for Fast Path to work.
- */
- mp1->b_rptr -= dle->dle_xtra_hdr_len;
- return mp1;
- }
-
- /* Create a DL_ERROR_ACK for the DLPI primitive in "mp". Called from dle_wput. */
- static mblk_t *
- dle_wput_error (queue_t * q, mblk_t * mp, int dl_err, int sys_err)
- {
- dl_error_ack_t * dlea = (dl_error_ack_t *)mp->b_rptr;
- size_t len = mp->b_wptr - mp->b_rptr;
- UInt32 primitive;
-
- if (len < sizeof(dlea->dl_primitive)) {
- freemsg(mp);
- return nilp(mblk_t);
- }
- primitive = dlea->dl_primitive;
- freemsg(mp);
- mp = allocb(sizeof(*dlea), BPRI_HI);
- if (mp == nilp(mblk_t))
- return nilp(mblk_t);
- mp->b_datap->db_type = M_PCPROTO;
- dlea = (dl_error_ack_t *)mp->b_rptr;
- mp->b_wptr = (UInt8 *)&dlea[1];
- dlea->dl_primitive = DL_ERROR_ACK;
- dlea->dl_error_primitive = primitive;
- dlea->dl_errno = dl_err;
- dlea->dl_unix_errno = sys_err;
- return mp;
- }
-
- /* Handle all M_IOCTL messages for dle_wput. */
- static mblk_t *
- dle_wput_ioctl (queue_t * q, mblk_t * mp)
- {
- mblk_t * mp1, * mp2;
- struct iocblk * ioc;
- dcl_t * dcl;
-
- dcl = (dcl_t *)q->q_ptr;
- ioc = (struct iocblk *)mp->b_rptr;
- switch (ioc->ioc_cmd) {
- case DL_IOC_HDR_INFO:
- /*
- * Fast-path request. First, allocate an mblk
- * to be used for the generated headers. This mblk
- * is passed back to dle_wput as a zero-length
- * data packet.
- */
- mp2 = allocb(128, BPRI_LO);
- if (mp2 == nilp(mblk_t))
- break;
- /* Make all the space available for headers. */
- mp2->b_wptr = mp2->b_datap->db_lim;
- mp2->b_rptr = mp2->b_wptr;
-
- mp1 = copyb(mp->b_cont);
- if ( mp1 == nilp(mblk_t) ) {
- freeb(mp2);
- break;
- }
-
- mp1->b_datap->db_type = M_PROTO;
- mp1->b_cont = mp2;
-
- /*
- * Call dle_wput back with a "normal" DL_UNITDATA_REQ
- * message. The returned mblk will contain the
- * header to be used in fast-path M_DATA messages.
- */
- mp1 = dle_wput(q, mp1);
- if (mp1 == nilp(mblk_t))
- break;
- if (mp1->b_datap->db_type != M_DATA) {
- freemsg(mp1);
- break;
- }
- mp->b_cont->b_cont = mp1;
-
- /*
- * Clear the per-stream flag so that inbound
- * non-multicast/broadcast packets will be
- * passed upstream as M_DATA messages.
- */
- dcl->dcl_flags &= ~F_DCL_NOT_FAST_PATH;
- ok_ioctl:;
- ioc->ioc_count = 0;
- for (mp1 = mp; (mp1 = mp1->b_cont) != nilp(mblk_t); )
- ioc->ioc_count += mp1->b_wptr - mp1->b_rptr;
- ioc->ioc_error = 0;
- mp->b_datap->db_type = M_IOCACK;
- return mp;
-
- case I_OTSetFramingType:
- /*
- * Open Transport ioctl. As specified in the OT documentation,
- * setting the framing type only changes the mac type returned
- * in DL_INFO_ACK messages. Outbound packets are formatted
- * according to the sap bound to the stream.
- */
- mp1 = mp->b_cont;
- if (mp1 == nilp(mblk_t) || (mp1->b_wptr - mp1->b_rptr) != sizeof(UInt32))
- break;
- {
- UInt32 framing_type = *(UInt32 *)mp1->b_rptr;
-
- if (framing_type != kOTGetFramingValue) {
- dcl->dcl_framing_type = framing_type;
- if (framing_type == kOTFraming8022)
- dcl->dcl_mac_type = DL_CSMACD;
- else
- dcl->dcl_mac_type = DL_ETHER;
- }
- }
- mp->b_cont = nilp(mblk_t);
- freeb(mp1);
- ioc->ioc_rval = dcl->dcl_framing_type;
- goto ok_ioctl;
-
- case I_OTSetRawMode:
- {
- dle_t * dle = dcl_to_dle(dcl);
- dl_recv_control_t * dlrc;
- unsigned long len;
-
- mp1 = mp->b_cont;
- if (mp1 == nilp(mblk_t))
- break;
- len = mp1->b_wptr - mp1->b_rptr;
- if (len < sizeof(dlrc->dl_primitive))
- break;
- dlrc = (dl_recv_control_t *)mp1->b_rptr;
- switch (dlrc->dl_primitive) {
- case kOTSetRecvMode:
- {
- unsigned int flags;
- unsigned int dflags;
-
- if (len < sizeof(dl_recv_control_t))
- break;
-
- flags = dlrc->dl_flags;
- dflags = dcl->dcl_flags & ~(F_DCL_WANTS_RS_HEADER | F_DCL_WANTS_ERROR_PACKETS);
-
- len = dcl_to_dle(dcl)->dle_istatus.mtu;
- if (flags & DL_TRUNCATED_PACKET) {
- if (dlrc->dl_truncation_length < len)
- len = dlrc->dl_truncation_length;
- }
- dcl->dcl_truncation_length = len;
- dlrc->dl_truncation_length = len;
-
- if (flags & DL_NORMAL_STATUS) {
- dflags |= F_DCL_WANTS_RS_HEADER;
- if (flags & DL_ERROR_STATUS) {
- if (dle->dle_hw.dlehw_recv_error_flags)
- dlrc->dl_flags |= dle->dle_hw.dlehw_recv_error_flags;
- dlrc->dl_flags |= DL_BAD_802_3_LENGTH;
- dflags |= F_DCL_WANTS_ERROR_PACKETS;
- }
- } else if (flags != 0) {
- break;
- }
-
- dcl->dcl_flags = dflags;
- }
- ioc->ioc_rval = sizeof(dl_recv_status_t);
- goto ok_ioctl;
-
- case kOTSendErrorPacket:
- {
- dl_send_control_t * dlsc;
-
- if (len < sizeof(dl_send_control_t))
- break;
- dlsc = (dl_send_control_t *)dlrc;
- mp1->b_rptr += sizeof(dl_send_control_t);
-
- mp->b_cont = nilp(mblk_t);
- ioc->ioc_count = 0;
- ioc->ioc_error = 0;
- ioc->ioc_rval = 0;
- mp->b_datap->db_type = M_IOCACK;
-
- if (dlsc->dl_flags & DL_BAD_802_3_LENGTH) {
- qreply(q, mp);
- return mp1;
- }
-
- if (dle->dle_hw.dlehw_send_error) {
- ioc->ioc_error = (*dle->dle_hw.dlehw_send_error)(
- dle, mp1, dlsc->dl_flags);
- } else
- ioc->ioc_error = EINVAL;
- return mp;
- }
- default:
- break;
- }
- }
- break;
-
- default:
- /* Only NAK the ioctl if we don't recognize the command. */
- mp->b_datap->db_type = M_IOCNAK;
- ioc->ioc_error = EINVAL;
- return mp;
- }
- /* If there is a formatting error, then ACK the ioctl with an error. */
- mp->b_datap->db_type = M_IOCACK;
- ioc->ioc_error = EINVAL;
- return mp;
- }
-
- /* Handle all DLPI primitives except DL_UNITDATA_REQ from dle_wput. */
- static mblk_t *
- dle_wput_proto (queue_t * q, mblk_t * mp)
- {
- mblk_t * mp1;
- size_t len;
- dl_info_req_t * dlir = (dl_info_req_t *)mp->b_rptr;
- dle_dlpi_dispatch_t * dledd = dle_dlpi_dispatch_table;
- int err;
-
- err = DL_BADPRIM;
- len = mp->b_wptr - mp->b_rptr;
- if (len >= sizeof(dlir->dl_primitive)) {
- /*
- * Search the DLPI dispatch table for the routine to
- * handle this primitive.
- */
- for ( ; dledd < A_END(dle_dlpi_dispatch_table); dledd++ ) {
- if ( dledd->dledd_primitive == dlir->dl_primitive ) {
- if (len < dledd->dledd_min_len)
- break;
- /* Create an mblk to hold the ACK message. */
- mp1 = mi_reallocb(mp, dledd->dledd_reallocb_len);
- if (mp1 == nilp(mblk_t)) {
- err = DL_SYSERR;
- break;
- }
- mp = mp1;
- err = (*dledd->dledd_func)(q, mp);
- break;
- }
- }
- }
-
- /*
- * If we couldn't find the primitive or the handler returned an error,
- * then create a DL_ERROR_ACK message to go upstream.
- */
- if (err != 0) {
- return dle_wput_error(q, mp, err
- , (err == DL_SYSERR) ? ENOMEM : 0);
- }
-
- /*
- * If there was no error, but the primitive requires a DL_OK_ACK
- * message, then create the ACK and pass it upstream.
- */
- if (dledd->dledd_flags & F_MIEDD_OK_ACK_NEEDED) {
- dl_ok_ack_t * dloa = (dl_ok_ack_t *)mp->b_rptr;
-
- dloa->dl_correct_primitive = dloa->dl_primitive;
- dloa->dl_primitive = DL_OK_ACK;
- mp->b_wptr = (UInt8 *)&dloa[1];
- mp->b_datap->db_type = M_PCPROTO;
- return mp;
- }
-
- /* Return NULL since we have consumed the message. */
- return nilp(mblk_t);
- }
-
- /*
- * Create a DL_UDERROR_IND message for dle_wput. This routine may also
- * be called directly from board-specific code.
- */
- mblk_t *
- dle_wput_ud_error (mblk_t * mp, int dlpi_err, int unix_err)
- {
- UInt32 dest_addr_len;
- UInt8 local_addr[8];
- UInt8 * dst;
- dl_uderror_ind_t * dlud;
- mblk_t * mp1;
-
- strlog(0, 0, 1, SL_TRACE, "dle_wput_ud_error: dlpi_err %d, unix_err %d\n"
- , dlpi_err, unix_err);
-
- switch (mp->b_datap->db_type) {
- case M_PROTO:
- case M_PCPROTO:
- {
- dl_unitdata_req_t * dlur = (dl_unitdata_req_t *)mp->b_rptr;
- size_t len = mp->b_wptr - mp->b_rptr;
- UInt32 dest_addr_offset = dlur->dl_dest_addr_offset;
-
- dest_addr_len = dlur->dl_dest_addr_length;
- len -= dest_addr_offset;
- dst = &mp->b_rptr[dest_addr_offset];
- if (len < dest_addr_len)
- dest_addr_len = 0;
- }
- break;
- case M_DATA:
- bcopy(mp->b_rptr, local_addr, 6);
- bcopy(mp->b_rptr + 12, local_addr + 6, 2);
- dest_addr_len = 8;
- dst = local_addr;
- break;
- default:
- freemsg(mp);
- return nilp(mblk_t);
- }
- mp1 = allocb(sizeof(dl_uderror_ind_t) + dest_addr_len, BPRI_HI);
- if (mp1 == nilp(mblk_t)) {
- freemsg(mp);
- return nilp(mblk_t);;
- }
- mp1->b_datap->db_type = M_PCPROTO;
- mp1->b_wptr = &mp1->b_rptr[sizeof(dl_uderror_ind_t) + dest_addr_len];
- dlud = (dl_uderror_ind_t *)mp1->b_rptr;
- dlud->dl_primitive = DL_UDERROR_IND;
- dlud->dl_dest_addr_length = dest_addr_len;
- dlud->dl_dest_addr_offset = sizeof(dl_uderror_ind_t);
- dlud->dl_unix_errno = unix_err;
- dlud->dl_errno = dlpi_err;
- if (dest_addr_len != 0)
- bcopy(dst, (char *)&dlud[1], dest_addr_len);
- freemsg(mp);
- return mp1;
- }
-
- /*
- * Handle DL_TEST_REQ, DL_TEST_RES, DL_XID_REQ and DL_XID_RES messages
- * from dle_wput. All of these messages have the same format with a
- * different DLPI primitive.
- */
- static int
- dle_xidtest (queue_t * q, mblk_t * mp)
- {
- dcl_t * dcl = (dcl_t *)q->q_ptr;
- dle_t * dle;
- dl_test_req_t * dtr;
- dl_unitdata_req_t * dlur;
- UInt32 saved_flags;
- UInt32 primitive;
- UInt8 * rptr;
- UInt32 test_flag;
- UInt8 control_value;
-
- if (dcl->dcl_state != DL_IDLE)
- return DL_OUTSTATE;
- dle = dcl_to_dle(dcl);
-
- /* We can't send 802 test messages on non-802 streams. */
- if (!(dcl->dcl_flags & F_DCL_802))
- return DL_NOTSUPPORTED;
-
- /*
- * Make sure that the message is properly formatted and
- * we have the address bytes needed.
- */
- dtr = (dl_test_req_t *)mp->b_rptr;
- if (!mi_offset_param(mp, dtr->dl_dest_addr_offset, dtr->dl_dest_addr_length))
- return DL_BADADDR;
-
- /*
- * If the user previously requested automatic handling of Test
- * or XID messages, then give him an error now.
- */
- primitive = dtr->dl_primitive; /* save for later */
- switch (primitive) {
- case DL_TEST_REQ:
- case DL_TEST_RES:
- if (dcl->dcl_flags & F_DCL_AUTO_TEST)
- return DL_TESTAUTO;
- control_value = LLC_TEST_VALUE;
- break;
- case DL_XID_REQ:
- case DL_XID_RES:
- if (dcl->dcl_flags & F_DCL_AUTO_XID)
- return DL_XIDAUTO;
- /*
- * If there is any data associated with the request,
- * then return an error. The 802.2 spec states that
- * XID packets contain only 3 bytes of LLC information,
- * and we, the LLC layer, are responsible for generating
- * those 3 bytes.
- */
- if (mp->b_cont != nilp(mblk_t)) {
- if (msgdsize(mp->b_cont) != 0)
- return DL_BADDATA;
- freemsg(mp->b_cont);
- mp->b_cont = nilp(mblk_t);
- }
-
- /* Allocate an M_DATA block for the XID information field. */
- {
- mblk_t * mp1 = allocb(3, BPRI_HI);
- if (mp1 == nilp(mblk_t))
- return DL_SYSERR;
- rptr = mp1->b_rptr;
- mp1->b_wptr = rptr + 3;
- rptr[0] = XID_FORMAT_IDENTIFIER;
- rptr[1] = 0x1; /* Type 1 LLC, non-NULL LSAP */
- rptr[2] = 0;
- mp->b_cont = mp1;
- }
- control_value = LLC_XID_VALUE;
- break;
- default:
- /* Shouldn't happen. */
- return DL_UNSUPPORTED;
- }
-
- /*
- * Convert the request/response to a DL_UNITDATA_REQ.
- * Ordering is important here. Note that we don't set
- * dl_priority here, and we rely on dle_wput not examining
- * that field.
- */
- dlur = (dl_unitdata_req_t *)dtr;
- test_flag = dtr->dl_flag; /* save for later */
- dlur->dl_primitive = DL_UNITDATA_REQ;
- dlur->dl_dest_addr_length = dtr->dl_dest_addr_length;
- dlur->dl_dest_addr_offset = dtr->dl_dest_addr_offset;
-
- /* If this is a SNAP-bound stream, pretend it isn't for dle_wput. */
- saved_flags = (dcl->dcl_flags & F_DCL_SNAP);
- dcl->dcl_flags &= ~F_DCL_SNAP;
-
- /* Call dle_wput to create the outbound M_DATA message. */
- mp = dle_wput(q, mp);
- dcl->dcl_flags |= saved_flags;
- if (mp == nilp(mblk_t))
- return DL_SYSERR;
-
- /* Copy the command information into the formatted packet. */
- rptr = &mp->b_rptr[dle->dle_xtra_hdr_len];
-
- if (saved_flags & F_BIND_SNAP)
- rptr[LLC_SSAP_OFFSET] = 0xAA;
- if (primitive == DL_TEST_RES || primitive == DL_XID_RES)
- rptr[LLC_SSAP_OFFSET] |= LLC_RESPONSE_BIT;
- rptr[LLC_CONTROL_OFFSET] = control_value;
- if (test_flag)
- rptr[LLC_CONTROL_OFFSET] |= LLC_POLL_FINAL_FLAG;
-
- /* Transmit the packet by a lateral to the board's put routine. */
- put(q, mp);
- return 0;
- }
-